From 807bc68008ac03511e43dd433519bb9432d471d9 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sun, 19 Mar 2006 08:11:19 +0000 Subject: [PATCH] Implemented PHP code generation for CBT templates. With this method, the new skin is 25% faster than the original monobook for anonymous page views. --- includes/cbt/CBTCompiler.php | 356 ++++++++++++++++++++++++++++ includes/{ => cbt}/CBTProcessor.php | 129 ++++------ includes/cbt/README | 55 +++++ skins/disabled/MonoBookCBT.php | 201 ++++++++-------- 4 files changed, 558 insertions(+), 183 deletions(-) create mode 100644 includes/cbt/CBTCompiler.php rename includes/{ => cbt}/CBTProcessor.php (75%) create mode 100644 includes/cbt/README diff --git a/includes/cbt/CBTCompiler.php b/includes/cbt/CBTCompiler.php new file mode 100644 index 0000000000..4b5bac058f --- /dev/null +++ b/includes/cbt/CBTCompiler.php @@ -0,0 +1,356 @@ +opcode = $opcode; + $this->arg1 = $arg1; + $this->arg2 = $arg2; + } + + function name() { + $opcodeNames = array( + CBT_PUSH => 'PUSH', + CBT_CAT => 'CAT', + CBT_CATS => 'CATS', + CBT_CALL => 'CALL', + CBT_HX => 'HX', + ); + return $opcodeNames[$this->opcode]; + } +}; + +class CBTCompiler { + var $mOps = array(); + var $mCode; + + function CBTCompiler( $text ) { + $this->mText = $text; + } + + function compile() { + $fname = 'CBTProcessor::compile'; + $this->mLastError = false; + $this->mOps = array(); + + $this->doText( 0, strlen( $this->mText ) ); + + if ( $this->mLastError !== false ) { + $pos = $this->mErrorPos; + + // Find the line number at which the error occurred + $startLine = 0; + $endLine = 0; + $line = 0; + do { + if ( $endLine ) { + $startLine = $endLine + 1; + } + $endLine = strpos( $this->mText, "\n", $startLine ); + ++$line; + } while ( $endLine !== false && $endLine < $pos ); + + $text = "Template error at line $line: $this->mLastError\n
\n";
+
+			$context = rtrim( str_replace( "\t", " ", substr( $this->mText, $startLine, $endLine - $startLine ) ) );
+			$text .= htmlspecialchars( $context ) . "\n" . str_repeat( ' ', $pos - $startLine ) . "^\n
\n"; + } else { + $text = true; + } + + return $text; + } + + /** Shortcut for doOpenText( $start, $end, false */ + function doText( $start, $end ) { + return $this->doOpenText( $start, $end, false ); + } + + function phpQuote( $text ) { + return "'" . strtr( $text, array( "\\" => "\\\\", "'" => "\\'" ) ) . "'"; + } + + function op( $opcode, $arg1 = null, $arg2 = null) { + return new CBTOp( $opcode, $arg1, $arg2 ); + } + + /** + * Recursive workhorse for text mode. + * + * Processes text mode starting from offset $p, until either $end is + * reached or a closing brace is found. If $needClosing is false, a + * closing brace will flag an error, if $needClosing is true, the lack + * of a closing brace will flag an error. + * + * The parameter $p is advanced to the position after the closing brace, + * or after the end. A CBTValue is returned. + * + * @private + */ + function doOpenText( &$p, $end, $needClosing = true ) { + $in =& $this->mText; + $start = $p; + $atStart = true; + + $foundClosing = false; + while ( $p < $end ) { + $matchLength = strcspn( $in, CBT_BRACE, $p, $end - $p ); + $pToken = $p + $matchLength; + + if ( $pToken >= $end ) { + // No more braces, output remainder + if ( $atStart ) { + $this->mOps[] = $this->op( CBT_PUSH, substr( $in, $p ) ); + $atStart = false; + } else { + $this->mOps[] = $this->op( CBT_CAT, substr( $in, $p ) ); + } + $p = $end; + break; + } + + // Output the text before the brace + if ( $atStart ) { + $this->mOps[] = $this->op( CBT_PUSH, substr( $in, $p, $matchLength ) ); + $atStart = false; + } else { + $this->mOps[] = $this->op( CBT_CAT, substr( $in, $p, $matchLength ) ); + } + + // Advance the pointer + $p = $pToken + 1; + + // Check for closing brace + if ( $in[$pToken] == '}' ) { + $foundClosing = true; + break; + } + + // Handle the "{fn}" special case + if ( $pToken > 0 && $in[$pToken-1] == '"' ) { + $this->doOpenFunction( $p, $end ); + if ( $p < $end && $in[$p] == '"' ) { + $this->mOps[] = $this->op( CBT_HX ); + } + } else { + $this->doOpenFunction( $p, $end ); + } + if ( $atStart ) { + $atStart = false; + } else { + $this->mOps[] = $this->op( CBT_CATS ); + } + } + if ( $foundClosing && !$needClosing ) { + $this->error( 'Errant closing brace', $p ); + } elseif ( !$foundClosing && $needClosing ) { + $this->error( 'Unclosed text section', $start ); + } else { + if ( $atStart ) { + $this->mOps[] = $this->op( CBT_PUSH, '' ); + } + } + } + + /** + * Recursive workhorse for function mode. + * + * Processes function mode starting from offset $p, until either $end is + * reached or a closing brace is found. If $needClosing is false, a + * closing brace will flag an error, if $needClosing is true, the lack + * of a closing brace will flag an error. + * + * The parameter $p is advanced to the position after the closing brace, + * or after the end. A CBTValue is returned. + * + * @private + */ + function doOpenFunction( &$p, $end, $needClosing = true ) { + $in =& $this->mText; + $start = $p; + $argCount = 0; + + $foundClosing = false; + while ( $p < $end ) { + $char = $in[$p]; + if ( $char == '{' ) { + // Switch to text mode + ++$p; + $tokenStart = $p; + $this->doOpenText( $p, $end ); + ++$argCount; + } elseif ( $char == '}' ) { + // Block end + ++$p; + $foundClosing = true; + break; + } elseif ( false !== strpos( CBT_WHITE, $char ) ) { + // Whitespace + // Consume the rest of the whitespace + $p += strspn( $in, CBT_WHITE, $p, $end - $p ); + } else { + // Token, find the end of it + $tokenLength = strcspn( $in, CBT_DELIM, $p, $end - $p ); + $this->mOps[] = $this->op( CBT_PUSH, substr( $in, $p, $tokenLength ) ); + + // Execute the token as a function if it's not the function name + if ( $argCount ) { + $this->mOps[] = $this->op( CBT_CALL, 1 ); + } + + $p += $tokenLength; + ++$argCount; + } + } + if ( !$foundClosing && $needClosing ) { + $this->error( 'Unclosed function', $start ); + return ''; + } + + $this->mOps[] = $this->op( CBT_CALL, $argCount ); + } + + /** + * Set a flag indicating that an error has been found. + */ + function error( $text, $pos = false ) { + $this->mLastError = $text; + if ( $pos === false ) { + $this->mErrorPos = $this->mCurrentPos; + } else { + $this->mErrorPos = $pos; + } + } + + function getLastError() { + return $this->mLastError; + } + + function opsToString() { + $s = ''; + foreach( $this->mOps as $op ) { + $s .= $op->name(); + if ( !is_null( $op->arg1 ) ) { + $s .= ' ' . var_export( $op->arg1, true ); + } + if ( !is_null( $op->arg2 ) ) { + $s .= ' ' . var_export( $op->arg2, true ); + } + $s .= "\n"; + } + return $s; + } + + function generatePHP( $functionObj ) { + $stack = array(); + + foreach( $this->mOps as $index => $op ) { + switch( $op->opcode ) { + case CBT_PUSH: + $stack[] = $this->phpQuote( $op->arg1 ); + break; + case CBT_CAT: + $val = array_pop( $stack ); + array_push( $stack, "$val . " . $this->phpQuote( $op->arg1 ) ); + break; + case CBT_CATS: + $right = array_pop( $stack ); + $left = array_pop( $stack ); + array_push( $stack, "$left . $right" ); + break; + case CBT_CALL: + $args = array_slice( $stack, count( $stack ) - $op->arg1, $op->arg1 ); + $stack = array_slice( $stack, 0, count( $stack ) - $op->arg1 ); + + // Some special optimised expansions + if ( $op->arg1 == 0 ) { + $result = ''; + } else { + $func = array_shift( $args ); + if ( substr( $func, 0, 1 ) == "'" && substr( $func, -1 ) == "'" ) { + $func = substr( $func, 1, strlen( $func ) - 2 ); + if ( $func == "if" ) { + if ( $op->arg1 < 3 ) { + // This should have been caught during processing + return "Not enough arguments to if"; + } elseif ( $op->arg1 == 3 ) { + $result = "(({$args[0]} != '') ? ({$args[1]}) : '')"; + } else { + $result = "(({$args[0]} != '') ? ({$args[1]}) : ({$args[2]}))"; + } + } elseif ( $func == "true" ) { + $result = "true"; + } elseif( $func == "lbrace" || $func == "{" ) { + $result = "{"; + } elseif( $func == "rbrace" || $func == "}" ) { + $result = "}"; + } elseif ( $func == "escape" || $func == "~" ) { + $result = "htmlspecialchars({$args[0]})"; + } else { + // Known function name + $result = "{$functionObj}->{$func}(" . implode( ', ', $args ) . ')->mText'; + } + } else { + // Unknown function name + $result = "call_user_func(array($functionObj, $func), " . implode( ', ', $args ) . ' )->mText'; + } + } + array_push( $stack, $result ); + break; + case CBT_HX: + $val = array_pop( $stack ); + array_push( $stack, "htmlspecialchars( $val )" ); + break; + default: + return "Unknown opcode {$op->opcode}\n"; + } + } + if ( count( $stack ) !== 1 ) { + return "Error, stack count incorrect\n"; + } + return $stack[0]; + } +} +?> diff --git a/includes/CBTProcessor.php b/includes/cbt/CBTProcessor.php similarity index 75% rename from includes/CBTProcessor.php rename to includes/cbt/CBTProcessor.php index 0840bb2d48..f6096e1e3c 100644 --- a/includes/CBTProcessor.php +++ b/includes/cbt/CBTProcessor.php @@ -4,69 +4,12 @@ * PHP version of the callback template processor * This is currently used as a test rig and is likely to be used for * compatibility purposes later, where the C++ extension is not available. - * - * Template syntax - * --------------- - * - * There are two modes: text mode and function mode. The brace characters "{" - * and "}" are the only reserved characters. Either one of them will switch from - * text mode to function mode wherever they appear, no exceptions. - * - * In text mode, all characters are passed through to the output. In function - * mode, text is split into tokens, delimited either by whitespace or by - * matching pairs of braces. The first token is taken to be a function name. The - * other tokens are first processed in function mode themselves, then they are - * passed to the named function as parameters. The return value of the function - * is passed through to the output. - * - * Example: - * {escape {"hello"}} - * - * First brace switches to function mode. The function name is escape, the first - * and only parameter is {"hello"}. This parameter is executed. The braces around - * the parameter cause the parser to switch to text mode, thus the string "hello", - * including the quotes, is passed back and used as an argument to the escape - * function. - * - * Example: - * {if title {

{title}

}} - * - * The function name is "if". The first parameter is the result of calling the - * function "title". The second parameter is a level 1 HTML heading containing - * the result of the function "title". "if" is a built-in function which will - * return the second parameter only if the first is non-blank, so the effect of - * this is to return a heading element only if a title exists. - * - * As a shortcut for generation of HTML attributes, if a function mode segment is - * surrounded by double quotes, quote characters in the return value will be - * escaped. This only applies if the quote character immediately precedes the - * opening brace, and immediately follows the closing brace, with no whitespace. - * - * User callback functions are defined by passing a function object to the - * template processor. Function names appearing in the text are first checked - * against built-in function names, then against the method names in the function - * object. The function object forms a sandbox for execution of the template, so - * security-conscious users may wish to avoid including functions that allow - * arbitrary filesystem access or code execution. - * - * The callback function will receive its parameters as strings. If the - * result of the function depends only on the arguments, and certain things - * understood to be "static", such as the source code, then the callback function - * should return a string. If the result depends on other things, then the function - * should return an object: - * - * return new TplValue( $text, $deps ); - * - * where $deps is an array of string tokens, each one naming a dependency. As a - * shortcut, if there is only one dependency, $deps may be a string. - * */ - -define( 'TP_WHITE', " \t\r\n" ); -define( 'TP_BRACE', '{}' ); -define( 'TP_DELIM', TP_WHITE . TP_BRACE ); -define( 'TP_DEBUG', 0 ); +define( 'CBT_WHITE', " \t\r\n" ); +define( 'CBT_BRACE', '{}' ); +define( 'CBT_DELIM', CBT_WHITE . CBT_BRACE ); +define( 'CBT_DEBUG', 0 ); /** * Attempting to be a MediaWiki-independent module @@ -90,7 +33,7 @@ function templateEscape( $text ) { * Callback functions should return one of these, unless they have * no dependencies in which case they can return a string. */ -class TplValue { +class CBTValue { var $mText, $mDeps, $mIsTemplate; /** @@ -99,7 +42,7 @@ class TplValue { * @param array $deps What this value depends on * @param bool $isTemplate whether the result needs compilation/execution */ - function TplValue( $text = '', $deps = array(), $isTemplate = false ) { + function CBTValue( $text = '', $deps = array(), $isTemplate = false ) { $this->mText = $text; if ( !is_array( $deps ) ) { $this->mDeps = array( $deps ) ; @@ -280,18 +223,20 @@ class CBTProcessor { * of a closing brace will flag an error. * * The parameter $p is advanced to the position after the closing brace, - * or after the end. A TplValue is returned. + * or after the end. A CBTValue is returned. * * @private */ function doOpenText( &$p, $end, $needClosing = true ) { + $fname = 'CBTProcessor::doOpenText'; + wfProfileIn( $fname ); $in =& $this->mText; $start = $p; - $ret = new TplValue( '', array(), $this->mCompiling ); + $ret = new CBTValue( '', array(), $this->mCompiling ); $foundClosing = false; while ( $p < $end ) { - $matchLength = strcspn( $in, TP_BRACE, $p, $end - $p ); + $matchLength = strcspn( $in, CBT_BRACE, $p, $end - $p ); $pToken = $p + $matchLength; if ( $pToken >= $end ) { @@ -315,14 +260,18 @@ class CBTProcessor { // Handle the "{fn}" special case if ( $pToken > 0 && $in[$pToken-1] == '"' ) { + wfProfileOut( $fname ); $val = $this->doOpenFunction( $p, $end ); + wfProfileIn( $fname ); if ( $p < $end && $in[$p] == '"' ) { $val->setText( htmlspecialchars( $val->getText() ) ); } $ret->cat( $val ); } else { // Process the function mode component + wfProfileOut( $fname ); $ret->cat( $this->doOpenFunction( $p, $end ) ); + wfProfileIn( $fname ); } } if ( $foundClosing && !$needClosing ) { @@ -330,6 +279,7 @@ class CBTProcessor { } elseif ( !$foundClosing && $needClosing ) { $this->error( 'Unclosed text section', $start ); } + wfProfileOut( $fname ); return $ret; } @@ -342,7 +292,7 @@ class CBTProcessor { * of a closing brace will flag an error. * * The parameter $p is advanced to the position after the closing brace, - * or after the end. A TplValue is returned. + * or after the end. A CBTValue is returned. * * @private */ @@ -367,14 +317,14 @@ class CBTProcessor { ++$p; $foundClosing = true; break; - } elseif ( false !== strpos( TP_WHITE, $char ) ) { + } elseif ( false !== strpos( CBT_WHITE, $char ) ) { // Whitespace // Consume the rest of the whitespace - $p += strspn( $in, TP_WHITE, $p, $end - $p ); + $p += strspn( $in, CBT_WHITE, $p, $end - $p ); } else { // Token, find the end of it - $tokenLength = strcspn( $in, TP_DELIM, $p, $end - $p ); - $token = new TplValue( substr( $in, $p, $tokenLength ) ); + $tokenLength = strcspn( $in, CBT_DELIM, $p, $end - $p ); + $token = new CBTValue( substr( $in, $p, $tokenLength ) ); // Execute the token as a function if it's not the function name if ( count( $tokens ) ) { $tokens[] = $this->doFunction( array( $token ), $p ); @@ -415,25 +365,25 @@ class CBTProcessor { // The dynamic parts of the string are still represented as functions, and // function invocations have no dependencies. Thus the compiled result has // no dependencies. - $val = new TplValue( "{{$compiled}}", array(), true ); + $val = new CBTValue( "{{$compiled}}", array(), true ); } return $val; } /** * Execute a function, caching and returning the result value. - * $tokens is an array of TplValue objects. $tokens[0] is the function + * $tokens is an array of CBTValue objects. $tokens[0] is the function * name, the others are arguments. $p is the string position, and is used * for error messages only. */ function doFunction( $tokens, $p ) { if ( count( $tokens ) == 0 ) { - return new TplValue; + return new CBTValue; } $fname = 'CBTProcessor::doFunction'; wfProfileIn( $fname ); - $ret = new TplValue; + $ret = new CBTValue; // All functions implicitly depend on their arguments, and the function name // While this is not strictly necessary for all functions, it's true almost @@ -470,13 +420,13 @@ class CBTProcessor { $this->mFunctionCache[$cacheKey] = $val; } else { $this->error( "Call of undefined function \"$func\"", $p ); - $val = new TplValue; + $val = new CBTValue; } if ( !is_object( $val ) ) { - $val = new TplValue((string)$val); + $val = new CBTValue((string)$val); } - if ( TP_DEBUG ) { + if ( CBT_DEBUG ) { $unexpanded = $val; } @@ -491,13 +441,14 @@ class CBTProcessor { $ret->addDeps( $val ); $ret->setText( $val->getText() ); - if ( TP_DEBUG ) { - print "doFunction $func args = "; - var_dump( $tokens ); - print "unexpanded return = "; - var_dump( $unexpanded ); - print "expanded return = "; - var_dump( $ret ); + if ( CBT_DEBUG ) { + wfDebug( "doFunction $func args = " + . var_export( $tokens, true ) + . "unexpanded return = " + . var_export( $unexpanded, true ) + . "expanded return = " + . var_export( $ret, true ) + ); } wfProfileOut( $fname ); @@ -528,16 +479,16 @@ class CBTProcessor { } if ( $condition->getText() != '' ) { - return new TplValue( $trueBlock->getText(), + return new CBTValue( $trueBlock->getText(), array_merge( $condition->getDeps(), $trueBlock->getDeps() ), $trueBlock->mIsTemplate ); } else { if ( !is_null( $falseBlock ) ) { - return new TplValue( $falseBlock->getText(), + return new CBTValue( $falseBlock->getText(), array_merge( $condition->getDeps(), $falseBlock->getDeps() ), $falseBlock->mIsTemplate ); } else { - return new TplValue( '', $condition->getDeps() ); + return new CBTValue( '', $condition->getDeps() ); } } } @@ -562,7 +513,7 @@ class CBTProcessor { * Escape text for inclusion in an HTML attribute */ function bi_escape( $val ) { - return new TplValue( htmlspecialchars( $val->getText() ), $val->getDeps() ); + return new CBTValue( htmlspecialchars( $val->getText() ), $val->getDeps() ); } } ?> diff --git a/includes/cbt/README b/includes/cbt/README new file mode 100644 index 0000000000..8be27d1d23 --- /dev/null +++ b/includes/cbt/README @@ -0,0 +1,55 @@ +Template syntax +--------------- + +There are two modes: text mode and function mode. The brace characters "{" +and "}" are the only reserved characters. Either one of them will switch from +text mode to function mode wherever they appear, no exceptions. + +In text mode, all characters are passed through to the output. In function +mode, text is split into tokens, delimited either by whitespace or by +matching pairs of braces. The first token is taken to be a function name. The +other tokens are first processed in function mode themselves, then they are +passed to the named function as parameters. The return value of the function +is passed through to the output. + +Example: + {escape {"hello"}} + +First brace switches to function mode. The function name is escape, the first +and only parameter is {"hello"}. This parameter is executed. The braces around +the parameter cause the parser to switch to text mode, thus the string "hello", +including the quotes, is passed back and used as an argument to the escape +function. + +Example: + {if title {

{title}

}} + +The function name is "if". The first parameter is the result of calling the +function "title". The second parameter is a level 1 HTML heading containing +the result of the function "title". "if" is a built-in function which will +return the second parameter only if the first is non-blank, so the effect of +this is to return a heading element only if a title exists. + +As a shortcut for generation of HTML attributes, if a function mode segment is +surrounded by double quotes, quote characters in the return value will be +escaped. This only applies if the quote character immediately precedes the +opening brace, and immediately follows the closing brace, with no whitespace. + +User callback functions are defined by passing a function object to the +template processor. Function names appearing in the text are first checked +against built-in function names, then against the method names in the function +object. The function object forms a sandbox for execution of the template, so +security-conscious users may wish to avoid including functions that allow +arbitrary filesystem access or code execution. + +The callback function will receive its parameters as strings. If the +result of the function depends only on the arguments, and certain things +understood to be "static", such as the source code, then the callback function +should return a string. If the result depends on other things, then the function +should return an object: + + return new CBTValue( $text, $deps ); + +where $deps is an array of string tokens, each one naming a dependency. As a +shortcut, if there is only one dependency, $deps may be a string. + diff --git a/skins/disabled/MonoBookCBT.php b/skins/disabled/MonoBookCBT.php index a5d5b54534..a0dace6ea3 100644 --- a/skins/disabled/MonoBookCBT.php +++ b/skins/disabled/MonoBookCBT.php @@ -4,7 +4,8 @@ if ( !defined( 'MEDIAWIKI' ) ) { die( "This file is part of MediaWiki, it is not a valid entry point\n" ); } -require_once( dirname(__FILE__) . '/../includes/CBTProcessor.php' ); +require_once( dirname(__FILE__) . '/../includes/cbt/CBTProcessor.php' ); +require_once( dirname(__FILE__) . '/../includes/cbt/CBTCompiler.php' ); require_once( dirname(__FILE__) . '/../includes/SkinTemplate.php' ); @@ -26,8 +27,6 @@ require_once( dirname(__FILE__) . '/../includes/SkinTemplate.php' ); * much as possible. In fact, the only SkinTemplate dependencies I know of at the * moment are the functions to generate the gen=css and gen=js files. * - * It assumes that $wgMemc is valid, if not, performance will be glacial as it will - * compile the skin on every invocation. */ class SkinMonoBookCBT extends SkinTemplate { var $mOut, $mTitle; @@ -62,7 +61,9 @@ class SkinMonoBookCBT extends SkinTemplate { $text = $this->executeTemplate( $template ); } else { $compiled = $this->getCompiledTemplate( $sourceFile ); - $text = $this->executeTemplate( $compiled ); + + #$text = $this->executeTemplate( $compiled ); + $text = eval( $compiled ); } wfProfileOut( $fname ); return $text; @@ -94,9 +95,9 @@ class SkinMonoBookCBT extends SkinTemplate { $recompile = $wgRequest->getVal( 'recompile' ); if ( !$recompile ) { - $compiled = $parserMemc->get( $cacheKey ); + $php = $parserMemc->get( $cacheKey ); } - if ( $recompile || !$compiled ) { + if ( $recompile || !$php ) { $template = file_get_contents( $sourceFile ); $ignore = array( 'lang', 'loggedin', 'user' ); @@ -124,10 +125,22 @@ class SkinMonoBookCBT extends SkinTemplate { // more sure it is safe here. $compiled = preg_replace( '/^[ \t]+/m', '', $compiled ); $compiled = preg_replace( '/[\r\n]+/', "\n", $compiled ); - $parserMemc->set( $cacheKey, $compiled, 3600 ); + + // Compile to PHP + $compiler = new CBTCompiler( $compiled ); + $compiler->compile(); + $php = 'return ' . $compiler->generatePHP( '$this' ) . ";\n"; + /* + if ( !php_check_syntax( $php, $error ) ) { + print "$error
" . htmlspecialchars( $php )  . '
'; + exit; + }*/ + + + $parserMemc->set( $cacheKey, $php, 3600 ); } wfProfileOut( $fname ); - return $compiled; + return $php; } function executeTemplate( $template ) { @@ -155,14 +168,14 @@ class SkinMonoBookCBT extends SkinTemplate { function mimetype() { return $GLOBALS['wgMimeType']; } function charset() { return $GLOBALS['wgOutputEncoding']; } function headlinks() { - return new TplValue( $this->mOut->getHeadLinks(), 'dynamic' ); + return new CBTValue( $this->mOut->getHeadLinks(), 'dynamic' ); } function headscripts() { - return new TplValue( $this->mOut->getScript(), 'dynamic' ); + return new CBTValue( $this->mOut->getScript(), 'dynamic' ); } function pagetitle() { - return new TplValue( $this->mOut->getHTMLTitle(), array( 'title', 'lang' ) ); + return new CBTValue( $this->mOut->getHTMLTitle(), array( 'title', 'lang' ) ); } function stylepath() { return $GLOBALS['wgStylePath']; } @@ -170,7 +183,7 @@ class SkinMonoBookCBT extends SkinTemplate { function notprintable() { global $wgRequest; - return new TplValue( !$wgRequest->getBool( 'printable' ), 'nonview dynamic' ); + return new CBTValue( !$wgRequest->getBool( 'printable' ), 'nonview dynamic' ); } function jsmimetype() { return $GLOBALS['wgJsMimeType']; } @@ -184,7 +197,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $url = $this->makeUrl('-','action=raw&gen=js'); } - return new TplValue( $url, 'loggedin' ); + return new CBTValue( $url, 'loggedin' ); } function pagecss() { @@ -194,7 +207,7 @@ class SkinMonoBookCBT extends SkinTemplate { wfRunHooks( 'SkinTemplateSetupPageCss', array( &$out ) ); // Unknown dependencies - return new TplValue( $out, 'dynamic' ); + return new CBTValue( $out, 'dynamic' ); } function usercss() { @@ -207,7 +220,7 @@ class SkinMonoBookCBT extends SkinTemplate { } // Dynamic when not an ordinary page view, also depends on the username - return new TplValue( $usercss, array( 'nonview dynamic', 'user' ) ); + return new CBTValue( $usercss, array( 'nonview dynamic', 'user' ) ); } function sitecss() { @@ -257,12 +270,12 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $deps = array(); } - return new TplValue( $link, $deps, $isTemplate ); + return new CBTValue( $link, $deps, $isTemplate ); } function user_touched() { global $wgUser; - return new TplValue( $wgUser->mTouched, 'dynamic' ); + return new CBTValue( $wgUser->mTouched, 'dynamic' ); } function userjs() { @@ -274,7 +287,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $url = $this->makeUrl($this->getUserPageText().'/'.$this->mStyleName.'.js', 'action=raw&ctype='.$wgJsMimeType.'&dontcountme=s'); } - return new TplValue( $url, array( 'nonview dynamic', 'user' ) ); + return new CBTValue( $url, array( 'nonview dynamic', 'user' ) ); } function userjsprev() { @@ -285,7 +298,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $js = ''; } - return new TplValue( $js, array( 'nonview dynamic' ) ); + return new CBTValue( $js, array( 'nonview dynamic' ) ); } function trackbackhtml() { @@ -297,7 +310,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $tb = ''; } - return new TplValue( $tb, 'dynamic' ); + return new CBTValue( $tb, 'dynamic' ); } function body_ondblclick() { @@ -309,10 +322,10 @@ class SkinMonoBookCBT extends SkinTemplate { } if ( User::getDefaultOption('editondblclick') ) { - return new TplValue( $js, 'user', 'title' ); + return new CBTValue( $js, 'user', 'title' ); } else { // Optimise away for logged-out users - return new TplValue( $js, 'loggedin dynamic' ); + return new CBTValue( $js, 'loggedin dynamic' ); } } @@ -323,29 +336,29 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $js = ''; } - return new TplValue( $js, 'loggedin dynamic' ); + return new CBTValue( $js, 'loggedin dynamic' ); } function nsclass() { - return new TplValue( 'ns-' . $this->mTitle->getNamespace(), 'title' ); + return new CBTValue( 'ns-' . $this->mTitle->getNamespace(), 'title' ); } function sitenotice() { // Perhaps this could be given special dependencies using our knowledge of what // wfGetSiteNotice() depends on. - return new TplValue( wfGetSiteNotice(), 'dynamic' ); + return new CBTValue( wfGetSiteNotice(), 'dynamic' ); } function title() { - return new TplValue( $this->mOut->getPageTitle(), array( 'title', 'lang' ) ); + return new CBTValue( $this->mOut->getPageTitle(), array( 'title', 'lang' ) ); } function title_urlform() { - return new TplValue( $this->getThisTitleUrlForm(), 'title' ); + return new CBTValue( $this->getThisTitleUrlForm(), 'title' ); } function title_userurl() { - return new TplValue( urlencode( $this->mTitle->getDBkey() ), 'title' ); + return new CBTValue( urlencode( $this->mTitle->getDBkey() ), 'title' ); } function subtitle() { @@ -355,11 +368,11 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = $this->mOut->getSubtitle(); } - return new TplValue( $s, array( 'title', 'nonview dynamic' ) ); + return new CBTValue( $s, array( 'title', 'nonview dynamic' ) ); } function undelete() { - return new TplValue( $this->getUndeleteLink(), array( 'title', 'lang' ) ); + return new CBTValue( $this->getUndeleteLink(), array( 'title', 'lang' ) ); } function newtalk() { @@ -398,20 +411,20 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $ntl = ''; } - return new TplValue( $ntl, 'dynamic' ); + return new CBTValue( $ntl, 'dynamic' ); } function showjumplinks() { global $wgUser; - return new TplValue( $wgUser->getOption( 'showjumplinks' ) ? 'true' : '', 'user' ); + return new CBTValue( $wgUser->getOption( 'showjumplinks' ) ? 'true' : '', 'user' ); } function bodytext() { - return new TplValue( $this->mOut->getHTML(), 'dynamic' ); + return new CBTValue( $this->mOut->getHTML(), 'dynamic' ); } function catlinks() { - return new TplValue( $this->getCategories(), 'dynamic' ); + return new CBTValue( $this->getCategories(), 'dynamic' ); } function extratabs( $itemTemplate ) { @@ -434,19 +447,19 @@ class SkinMonoBookCBT extends SkinTemplate { $vcount ++; } } - return new TplValue( $s, array(), true ); + return new CBTValue( $s, array(), true ); } - function is_special() { return new TplValue( $this->mTitle->getNamespace() == NS_SPECIAL, 'title' ); } - function can_edit() { return new TplValue( (string)($this->mTitle->userCanEdit()), 'dynamic' ); } - function can_move() { return new TplValue( (string)($this->mTitle->userCanMove()), 'dynamic' ); } - function is_talk() { return new TplValue( (string)($this->mTitle->isTalkPage()), 'title' ); } - function is_protected() { return new TplValue( (string)$this->mTitle->isProtected(), 'dynamic' ); } - function nskey() { return new TplValue( $this->mTitle->getNamespaceKey(), 'title' ); } + function is_special() { return new CBTValue( $this->mTitle->getNamespace() == NS_SPECIAL, 'title' ); } + function can_edit() { return new CBTValue( (string)($this->mTitle->userCanEdit()), 'dynamic' ); } + function can_move() { return new CBTValue( (string)($this->mTitle->userCanMove()), 'dynamic' ); } + function is_talk() { return new CBTValue( (string)($this->mTitle->isTalkPage()), 'title' ); } + function is_protected() { return new CBTValue( (string)$this->mTitle->isProtected(), 'dynamic' ); } + function nskey() { return new CBTValue( $this->mTitle->getNamespaceKey(), 'title' ); } function request_url() { global $wgRequest; - return new TplValue( $wgRequest->getRequestURL(), 'dynamic' ); + return new CBTValue( $wgRequest->getRequestURL(), 'dynamic' ); } function subject_url() { @@ -456,7 +469,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $url = $title->getLocalUrl( 'action=edit' ); } - return new TplValue( $url, 'title' ); + return new CBTValue( $url, 'title' ); } function talk_url() { @@ -466,19 +479,19 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $url = $title->getLocalUrl( 'action=edit' ); } - return new TplValue( $url, 'title' ); + return new CBTValue( $url, 'title' ); } function edit_url() { - return new TplValue( $this->getEditUrl(), array( 'title', 'nonview dynamic' ) ); + return new CBTValue( $this->getEditUrl(), array( 'title', 'nonview dynamic' ) ); } function move_url() { - return new TplValue( $this->makeSpecialParamUrl( 'Movepage' ), array(), true ); + return new CBTValue( $this->makeSpecialParamUrl( 'Movepage' ), array(), true ); } function localurl( $query ) { - return new TplValue( $this->mTitle->getLocalURL( $query ), 'title' ); + return new CBTValue( $this->mTitle->getLocalURL( $query ), 'title' ); } function selecttab( $tab, $extraclass = '' ) { @@ -528,19 +541,19 @@ class SkinMonoBookCBT extends SkinTemplate { $s = ''; } } - return new TplValue( $s, array( 'nonview dynamic', 'title' ) ); + return new CBTValue( $s, array( 'nonview dynamic', 'title' ) ); } function subject_newclass() { $title = $this->getSubjectPage(); $class = $title->exists() ? '' : 'new'; - return new TplValue( $class, 'dynamic' ); + return new CBTValue( $class, 'dynamic' ); } function talk_newclass() { $title = $this->getTalkPage(); $class = $title->exists() ? '' : 'new'; - return new TplValue( $class, 'dynamic' ); + return new CBTValue( $class, 'dynamic' ); } function ca_variant( $code, $name, $index, $template ) { @@ -556,11 +569,11 @@ class SkinMonoBookCBT extends SkinTemplate { '$text' => $name, '$href' => htmlspecialchars( $this->mTitle->getLocalUrl( $actstr . 'variant=' . $code ) ) )); - return new TplValue( $s, 'dynamic' ); + return new CBTValue( $s, 'dynamic' ); } function is_watching() { - return new TplValue( (string)$this->mTitle->userIsWatching(), array( 'dynamic' ) ); + return new CBTValue( (string)$this->mTitle->userIsWatching(), array( 'dynamic' ) ); } @@ -592,7 +605,7 @@ class SkinMonoBookCBT extends SkinTemplate { $s .= "{login {{$etpl}}\n"; } // No dependencies - return new TplValue( $s, array(), true /*this is a template*/ ); + return new CBTValue( $s, array(), true /*this is a template*/ ); } function userpage( $itemTemplate ) { @@ -603,7 +616,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'user' ); + return new CBTValue( $s, 'user' ); } function mytalk( $itemTemplate ) { @@ -615,7 +628,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'user' ); + return new CBTValue( $s, 'user' ); } function preferences( $itemTemplate ) { @@ -625,7 +638,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, array( 'loggedin', 'lang' ) ); + return new CBTValue( $s, array( 'loggedin', 'lang' ) ); } function watchlist( $itemTemplate ) { @@ -635,7 +648,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, array( 'loggedin', 'lang' ) ); + return new CBTValue( $s, array( 'loggedin', 'lang' ) ); } function mycontris( $itemTemplate ) { @@ -646,7 +659,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'user' ); + return new CBTValue( $s, 'user' ); } function logout( $itemTemplate ) { @@ -658,7 +671,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'loggedin dynamic' ); + return new CBTValue( $s, 'loggedin dynamic' ); } function anonuserpage( $itemTemplate ) { @@ -669,7 +682,7 @@ class SkinMonoBookCBT extends SkinTemplate { $userPage = $this->getUserPageTitle(); $s = $this->makeTemplateLink( $itemTemplate, 'userpage', $userPage, $wgUser->getName() ); } - return new TplValue( $s, '!loggedin dynamic' ); + return new CBTValue( $s, '!loggedin dynamic' ); } function anontalk( $itemTemplate ) { @@ -680,7 +693,7 @@ class SkinMonoBookCBT extends SkinTemplate { $talkPage = $userPage->getTalkPage(); $s = $this->makeTemplateLink( $itemTemplate, 'mytalk', $talkPage, wfMsg('anontalk') ); } - return new TplValue( $s, '!loggedin dynamic' ); + return new CBTValue( $s, '!loggedin dynamic' ); } function anonlogin( $itemTemplate ) { @@ -690,7 +703,7 @@ class SkinMonoBookCBT extends SkinTemplate { $s = $this->makeSpecialTemplateLink( $itemTemplate, 'anonlogin', 'Userlogin', wfMsg( 'userlogin' ), 'returnto=' . urlencode( $this->getThisPDBK() ) ); } - return new TplValue( $s, '!loggedin dynamic' ); + return new CBTValue( $s, '!loggedin dynamic' ); } function login( $itemTemplate ) { @@ -700,7 +713,7 @@ class SkinMonoBookCBT extends SkinTemplate { $s = $this->makeSpecialTemplateLink( $itemTemplate, 'login', 'Userlogin', wfMsg( 'userlogin' ), 'returnto=' . urlencode( $this->getThisPDBK() ) ); } - return new TplValue( $s, '!loggedin dynamic' ); + return new CBTValue( $s, '!loggedin dynamic' ); } function logopath() { return $GLOBALS['wgLogo']; } @@ -756,7 +769,7 @@ class SkinMonoBookCBT extends SkinTemplate { } // Depends on user language only - return new TplValue( $s, 'lang' ); + return new CBTValue( $s, 'lang' ); } function searchaction() { @@ -766,23 +779,23 @@ class SkinMonoBookCBT extends SkinTemplate { function search() { global $wgRequest; - return new TplValue( trim( $this->getSearch() ), 'special dynamic' ); + return new CBTValue( trim( $this->getSearch() ), 'special dynamic' ); } function notspecialpage() { - return new TplValue( $this->mTitle->getNamespace() != NS_SPECIAL, 'special' ); + return new CBTValue( $this->mTitle->getNamespace() != NS_SPECIAL, 'special' ); } function nav_whatlinkshere() { - return new TplValue( $this->makeSpecialParamUrl('Whatlinkshere' ), array(), true ); + return new CBTValue( $this->makeSpecialParamUrl('Whatlinkshere' ), array(), true ); } function article_exists() { - return new TplValue( (string)($this->mTitle->getArticleId() !== 0), 'title' ); + return new CBTValue( (string)($this->mTitle->getArticleId() !== 0), 'title' ); } function nav_recentchangeslinked() { - return new TplValue( $this->makeSpecialParamUrl('Recentchangeslinked' ), array(), true ); + return new CBTValue( $this->makeSpecialParamUrl('Recentchangeslinked' ), array(), true ); } function feeds( $itemTemplate = '' ) { @@ -803,36 +816,36 @@ class SkinMonoBookCBT extends SkinTemplate { ) ); } } - return new TplValue( $feeds, 'special dynamic' ); + return new CBTValue( $feeds, 'special dynamic' ); } function is_userpage() { list( $id, $ip ) = $this->getUserPageIdIp(); - return new TplValue( (string)($id || $ip), 'title' ); + return new CBTValue( (string)($id || $ip), 'title' ); } function is_ns_mediawiki() { - return new TplValue( (string)$this->mTitle->getNamespace() == NS_MEDIAWIKI, 'title' ); + return new CBTValue( (string)$this->mTitle->getNamespace() == NS_MEDIAWIKI, 'title' ); } function is_loggedin() { global $wgUser; - return new TplValue( (string)($wgUser->isLoggedIn()), 'loggedin' ); + return new CBTValue( (string)($wgUser->isLoggedIn()), 'loggedin' ); } function nav_contributions() { $url = $this->makeSpecialParamUrl( 'Contributions', '', '{title_userurl}' ); - return new TplValue( $url, array(), true ); + return new CBTValue( $url, array(), true ); } function is_allowed( $right ) { global $wgUser; - return new TplValue( (string)$wgUser->isAllowed( $right ), 'user' ); + return new CBTValue( (string)$wgUser->isAllowed( $right ), 'user' ); } function nav_blockip() { $url = $this->makeSpecialParamUrl( 'Blockip', '', '{title_userurl}' ); - return new TplValue( $url, array(), true ); + return new CBTValue( $url, array(), true ); } function nav_emailuser() { @@ -840,7 +853,7 @@ class SkinMonoBookCBT extends SkinTemplate { if ( !$wgEnableEmail || !$wgEnableUserEmail ) return ''; $url = $this->makeSpecialParamUrl( 'Emailuser', '', '{title_userurl}' ); - return new TplValue( $url, array(), true ); + return new CBTValue( $url, array(), true ); } function nav_upload() { @@ -870,23 +883,23 @@ class SkinMonoBookCBT extends SkinTemplate { $url = $wgRequest->appendQuery( 'printable=yes' ); } } - return new TplValue( $url, array( 'nonview dynamic', 'title' ) ); + return new CBTValue( $url, array( 'nonview dynamic', 'title' ) ); } function nav_permalink() { $url = (string)$this->getPermalink(); - return new TplValue( $url, 'dynamic' ); + return new CBTValue( $url, 'dynamic' ); } function nav_trackbacklink() { global $wgUseTrackbacks; if ( !$wgUseTrackbacks ) return ''; - return new TplValue( $this->mTitle->trackbackURL(), 'title' ); + return new CBTValue( $this->mTitle->trackbackURL(), 'title' ); } function is_permalink() { - return new TplValue( (string)($this->getPermalink() === false), 'nonview dynamic' ); + return new CBTValue( (string)($this->getPermalink() === false), 'nonview dynamic' ); } function toolboxend() { @@ -915,7 +928,7 @@ class SkinMonoBookCBT extends SkinTemplate { } $s = str_replace( '$body', $s, $outer ); } - return new TplValue( $s, 'dynamic' ); + return new CBTValue( $s, 'dynamic' ); } function poweredbyico() { return $this->getPoweredBy(); } @@ -930,7 +943,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'dynamic' ); + return new CBTValue( $s, 'dynamic' ); } function viewcount() { @@ -948,7 +961,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $viewcount = ''; } - return new TplValue( $viewcount, 'dynamic' ); + return new CBTValue( $viewcount, 'dynamic' ); } function numberofwatchingusers() { @@ -968,7 +981,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $s = ''; } - return new TplValue( $s, 'dynamic' ); + return new CBTValue( $s, 'dynamic' ); } function credits() { @@ -982,7 +995,7 @@ class SkinMonoBookCBT extends SkinTemplate { } else { $credits = ''; } - return new TplValue( $credits, 'view dynamic' ); + return new CBTValue( $credits, 'view dynamic' ); } function normalcopyright() { @@ -995,7 +1008,7 @@ class SkinMonoBookCBT extends SkinTemplate { function is_currentview() { global $wgRequest; - return new TplValue( (string)$this->isCurrentArticleView(), 'view' ); + return new CBTValue( (string)$this->isCurrentArticleView(), 'view' ); } function usehistorycopyright() { @@ -1005,17 +1018,17 @@ class SkinMonoBookCBT extends SkinTemplate { $oldid = $this->getOldId(); $diff = $this->getDiff(); $use = (string)(!is_null( $oldid ) && is_null( $diff )); - return new TplValue( $use, 'nonview dynamic' ); + return new CBTValue( $use, 'nonview dynamic' ); } function privacy() { - return new TplValue( $this->privacyLink(), 'lang' ); + return new CBTValue( $this->privacyLink(), 'lang' ); } function about() { - return new TplValue( $this->aboutLink(), 'lang' ); + return new CBTValue( $this->aboutLink(), 'lang' ); } function disclaimer() { - return new TplValue( $this->disclaimerLink(), 'lang' ); + return new CBTValue( $this->disclaimerLink(), 'lang' ); } function tagline() { # A reference to this tag existed in the old MonoBook.php, but the @@ -1023,11 +1036,11 @@ class SkinMonoBookCBT extends SkinTemplate { return ''; } function reporttime() { - return new TplValue( $this->mOut->reportTime(), 'dynamic' ); + return new CBTValue( $this->mOut->reportTime(), 'dynamic' ); } function msg( $name ) { - return new TplValue( wfMsg( $name ), 'lang' ); + return new CBTValue( wfMsg( $name ), 'lang' ); } function fallbackmsg( $name, $fallback ) { @@ -1035,7 +1048,7 @@ class SkinMonoBookCBT extends SkinTemplate { if ( wfEmptyMsg( $name, $text ) ) { $text = $fallback; } - return new TplValue( $text, 'lang' ); + return new CBTValue( $text, 'lang' ); } /****************************************************** -- 2.20.1