Index: includes/CoreParserFunctions.php =================================================================== --- includes/CoreParserFunctions.php (revision 32931) +++ includes/CoreParserFunctions.php (working copy) @@ -41,6 +41,7 @@ $parser->setFunctionHook( 'special', array( __CLASS__, 'special' ) ); $parser->setFunctionHook( 'defaultsort', array( __CLASS__, 'defaultsort' ), SFH_NO_HASH ); $parser->setFunctionHook( 'filepath', array( __CLASS__, 'filepath' ), SFH_NO_HASH ); + $parser->setFunctionHook( 'pagesincategory', array( __CLASS__, 'pagesincategory' ), SFH_NO_HASH ); $parser->setFunctionHook( 'tag', array( __CLASS__, 'tagObj' ), SFH_OBJECT_ARGS ); if ( $wgAllowDisplayTitle ) { @@ -214,6 +215,23 @@ static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) { return self::formatRaw( SiteStats::pagesInNs( intval( $namespace ) ), $raw ); } + + static function pagesincategory( $parser, $category = '', $raw = null ) { + global $wgExpensiveParserFunctionLimit; + if ($category == '') { + return 0; + } + $parser->mExpensiveFunctionCount++; + if ($parser->mExpensiveFunctionCount <= $wgExpensiveParserFunctionLimit) { + $category = Category::newFromName($category); + $count = $category->getPageCount(); + if ( !$count ) { + $count = 0; + } + return self::formatRaw( $count, $raw ); + } + return 0; + } static function language( $parser, $arg = '' ) { global $wgContLang; Index: includes/DefaultSettings.php =================================================================== --- includes/DefaultSettings.php (revision 32931) +++ includes/DefaultSettings.php (working copy) @@ -3011,3 +3011,9 @@ * Special:Whatlinkshere/RedirectDestination */ $wgMaxRedirectLinksRetrieved = 500; + +/** +* Maximum number of calls to expensive parser functions +* such as PAGESINCATEGORY. +*/ +$wgExpensiveParserFunctionLimit = 100; Index: includes/MagicWord.php =================================================================== --- includes/MagicWord.php (revision 32931) +++ includes/MagicWord.php (working copy) @@ -102,6 +102,7 @@ 'pagesinnamespace', 'numberofadmins', 'defaultsort', + 'pagesincategory', ); /* Array of caching hints for ParserCache */ Index: includes/Parser.php =================================================================== --- includes/Parser.php (revision 32931) +++ includes/Parser.php (working copy) @@ -102,6 +102,7 @@ var $mIncludeSizes, $mPPNodeCount, $mDefaultSort; var $mTplExpandCache; // empty-frame expansion cache var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores; + var $mExpensiveFunctionCount; // number of expensive parser function calls # Temporary # These are variables reset at least once per parse regardless of $clearState @@ -217,6 +218,7 @@ $this->mDefaultSort = false; $this->mHeadings = array(); $this->mDoubleUnderscores = array(); + $this->mExpensiveFunctionCount = 0; # Fix cloning if ( isset( $this->mPreprocessor ) && $this->mPreprocessor->parser !== $this ) { @@ -390,17 +392,31 @@ array_values( $tidyregs ), $text ); } + global $wgExpensiveParserFunctionLimit; + if ( $this->mExpensiveFunctionCount > $wgExpensiveParserFunctionLimit ) { + if ( is_callable( array( $this->mOutput, 'addWarning' ) ) ) { + $warning = wfMsg( 'expensive-parserfunction-warning', $this->mExpensiveFunctionCount, $wgExpensiveParserFunctionLimit ); + $this->mOutput->addWarning( $warning ); + $cat = Title::makeTitleSafe( NS_CATEGORY, wfMsgForContent( 'expensive-parserfunction-category' ) ); + if ( $cat ) { + $this->mOutput->addCategory( $cat->getDBkey(), $this->getDefaultSort() ); + } + } + } wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) ); # Information on include size limits, for the benefit of users who try to skirt them if ( $this->mOptions->getEnableLimitReport() ) { + global $wgExpensiveParserFunctionLimit; $max = $this->mOptions->getMaxIncludeSize(); + $PFreport = "Expensive parser function count: {$this->mExpensiveFunctionCount}/$wgExpensiveParserFunctionLimit\n"; $limitReport = "NewPP limit report\n" . "Preprocessor node count: {$this->mPPNodeCount}/{$this->mOptions->mMaxPPNodeCount}\n" . "Post-expand include size: {$this->mIncludeSizes['post-expand']}/$max bytes\n" . - "Template argument size: {$this->mIncludeSizes['arg']}/$max bytes\n"; + "Template argument size: {$this->mIncludeSizes['arg']}/$max bytes\n". + $PFreport; wfRunHooks( 'ParserLimitReport', array( $this, &$limitReport ) ); $text .= "\n\n"; } Index: languages/messages/MessagesEn.php =================================================================== --- languages/messages/MessagesEn.php (revision 32931) +++ languages/messages/MessagesEn.php (working copy) @@ -338,6 +338,7 @@ 'filepath' => array( 0, 'FILEPATH:' ), 'tag' => array( 0, 'tag' ), 'hiddencat' => array( 1, '__HIDDENCAT__' ), + 'pagesincategory' => array( 1, 'PAGESINCATEGORY', 'PAGESINCAT' ), ); /** @@ -1128,7 +1129,11 @@ You should consider whether it is appropriate to continue editing this page. The deletion log for this page is provided here for convenience:", +'expensive-parserfunction-warning' => 'Warning: This page contains too many expensive parser function calls. +It should have less than $2, there are now $1.', +'expensive-parserfunction-category' => 'Pages with too many expensive parser function calls', + # "Undo" feature 'undo-success' => 'The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.', 'undo-failure' => 'The edit could not be undone due to conflicting intermediate edits.', Index: extensions/ParserFunctions/ParserFunctions.php =================================================================== --- extensions/ParserFunctions/ParserFunctions.php (revision 32931) +++ extensions/ParserFunctions/ParserFunctions.php (working copy) @@ -16,10 +16,7 @@ $wgExtensionMessagesFiles['ParserFunctions'] = dirname(__FILE__) . '/ParserFunctions.i18n.php'; $wgHooks['LanguageGetMagic'][] = 'wfParserFunctionsLanguageGetMagic'; -$wgHooks['ParserLimitReport'][] = 'wfParserFunctionsLimitReport'; -$wgMaxIfExistCount = 100; - class ExtParserFunctions { var $mExprParser; var $mTimeCache = array(); @@ -55,7 +52,6 @@ function clearState(&$parser) { $this->mTimeChars = 0; - $parser->pf_ifexist_count = 0; $parser->pf_ifexist_breakdown = array(); return true; } @@ -315,8 +311,8 @@ function incrementIfexistCount( $parser, $frame ) { // Don't let this be called more than a certain number of times. It tends to make the database explode. - global $wgMaxIfExistCount; - $parser->pf_ifexist_count++; + global $wgExpensiveParserFunctionLimit; + $parser->mExpensiveFunctionCount++; if ( $frame ) { $pdbk = $frame->getPDBK( 1 ); if ( !isset( $parser->pf_ifexist_breakdown[$pdbk] ) ) { @@ -324,7 +320,7 @@ } $parser->pf_ifexist_breakdown[$pdbk] ++; } - return $parser->pf_ifexist_count <= $wgMaxIfExistCount; + return $parser->mExpensiveFunctionCount <= $wgExpensiveParserFunctionLimit; } function ifexist( &$parser, $title = '', $then = '', $else = '' ) { @@ -357,15 +353,14 @@ } else { $pdbk = $title->getPrefixedDBkey(); $lc = LinkCache::singleton(); + if ( !$this->incrementIfexistCount( $parser, $frame ) ) { + return $else; + } if ( $lc->getGoodLinkID( $pdbk ) ) { return $then; } elseif ( $lc->isBadLink( $pdbk ) ) { return $else; } - if ( !$this->incrementIfexistCount( $parser, $frame ) ) { - return $else; - } - $id = $title->getArticleID(); $parser->mOutput->addLink( $title, $id ); if ( $id ) { @@ -476,22 +471,6 @@ return $title; } } - - function afterTidy( &$parser, &$text ) { - global $wgMaxIfExistCount; - if ( $parser->pf_ifexist_count > $wgMaxIfExistCount ) { - if ( is_callable( array( $parser->mOutput, 'addWarning' ) ) ) { - wfLoadExtensionMessages( 'ParserFunctions' ); - $warning = wfMsg( 'pfunc_ifexist_warning', $parser->pf_ifexist_count, $wgMaxIfExistCount ); - $parser->mOutput->addWarning( $warning ); - $cat = Title::makeTitleSafe( NS_CATEGORY, wfMsgForContent( 'pfunc_max_ifexist_category' ) ); - if ( $cat ) { - $parser->mOutput->addCategory( $cat->getDBkey(), $parser->getDefaultSort() ); - } - } - } - return true; - } } function wfSetupParserFunctions() { @@ -510,7 +489,6 @@ } $wgHooks['ParserClearState'][] = array( &$wgExtParserFunctions, 'clearState' ); - $wgHooks['ParserAfterTidy'][] = array( &$wgExtParserFunctions, 'afterTidy' ); } function wfParserFunctionsLanguageGetMagic( &$magicWords, $langCode ) { @@ -520,11 +498,3 @@ return true; } -function wfParserFunctionsLimitReport( $parser, &$report ) { - global $wgMaxIfExistCount; - if ( isset( $parser->pf_ifexist_count ) ) { - $report .= "#ifexist count: {$parser->pf_ifexist_count}/$wgMaxIfExistCount\n"; - } - return true; -} -