public function getFunctionStats() { $metrics = $this->xhprof->getCompleteMetrics(); $profile = array(); $main = null; // units in ms foreach ($metrics as $fname => $stats) { if ($this->shouldExclude($fname)) { continue; } // Convert elapsed times from μs to ms to match interface $entry = array('name' => $fname, 'calls' => $stats['ct'], 'real' => $stats['wt']['total'] / 1000, '%real' => $stats['wt']['percent'], 'cpu' => isset($stats['cpu']) ? $stats['cpu']['total'] / 1000 : 0, '%cpu' => isset($stats['cpu']) ? $stats['cpu']['percent'] : 0, 'memory' => isset($stats['mu']) ? $stats['mu']['total'] : 0, '%memory' => isset($stats['mu']) ? $stats['mu']['percent'] : 0, 'min_real' => $stats['wt']['min'] / 1000, 'max_real' => $stats['wt']['max'] / 1000); $profile[] = $entry; if ($fname === 'main()') { $main = $entry; } } // Merge in all of the custom profile sections foreach ($this->sprofiler->getFunctionStats() as $stats) { if ($this->shouldExclude($stats['name'])) { continue; } // @note: getFunctionStats() values already in ms $stats['%real'] = $main['real'] ? $stats['real'] / $main['real'] * 100 : 0; $stats['%cpu'] = $main['cpu'] ? $stats['cpu'] / $main['cpu'] * 100 : 0; $stats['%memory'] = $main['memory'] ? $stats['memory'] / $main['memory'] * 100 : 0; $profile[] = $stats; // assume no section names collide with $metrics } return $profile; }
/** * Convert wikitext to HTML * Do not call this function recursively. * * @param string $text Text we want to parse * @param Title $title * @param ParserOptions $options * @param bool $linestart * @param bool $clearState * @param int $revid Number to pass in {{REVISIONID}} * @return ParserOutput A ParserOutput */ public function parse($text, Title $title, ParserOptions $options, $linestart = true, $clearState = true, $revid = null) { /** * First pass--just handle <nowiki> sections, pass the rest off * to internalParse() which does all the real work. */ global $wgShowHostnames; if ($clearState) { // We use U+007F DELETE to construct strip markers, so we have to make // sure that this character does not occur in the input text. $text = strtr($text, "", "?"); $magicScopeVariable = $this->lock(); } $this->startParse($title, $options, self::OT_HTML, $clearState); $this->currentRevisionCache = null; $this->mInputSize = strlen($text); if ($this->mOptions->getEnableLimitReport()) { $this->mOutput->resetParseStartTime(); } $oldRevisionId = $this->mRevisionId; $oldRevisionObject = $this->mRevisionObject; $oldRevisionTimestamp = $this->mRevisionTimestamp; $oldRevisionUser = $this->mRevisionUser; $oldRevisionSize = $this->mRevisionSize; if ($revid !== null) { $this->mRevisionId = $revid; $this->mRevisionObject = null; $this->mRevisionTimestamp = null; $this->mRevisionUser = null; $this->mRevisionSize = null; } Hooks::run('ParserBeforeStrip', array(&$this, &$text, &$this->mStripState)); # No more strip! Hooks::run('ParserAfterStrip', array(&$this, &$text, &$this->mStripState)); $text = $this->internalParse($text); Hooks::run('ParserAfterParse', array(&$this, &$text, &$this->mStripState)); $text = $this->internalParseHalfParsed($text, true, $linestart); /** * A converted title will be provided in the output object if title and * content conversion are enabled, the article text does not contain * a conversion-suppressing double-underscore tag, and no * {{DISPLAYTITLE:...}} is present. DISPLAYTITLE takes precedence over * automatic link conversion. */ if (!($options->getDisableTitleConversion() || isset($this->mDoubleUnderscores['nocontentconvert']) || isset($this->mDoubleUnderscores['notitleconvert']) || $this->mOutput->getDisplayTitle() !== false)) { $convruletitle = $this->getConverterLanguage()->getConvRuleTitle(); if ($convruletitle) { $this->mOutput->setTitleText($convruletitle); } else { $titleText = $this->getConverterLanguage()->convertTitle($title); $this->mOutput->setTitleText($titleText); } } if ($this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit()) { $this->limitationWarn('expensive-parserfunction', $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit()); } # Information on include size limits, for the benefit of users who try to skirt them if ($this->mOptions->getEnableLimitReport()) { $max = $this->mOptions->getMaxIncludeSize(); $cpuTime = $this->mOutput->getTimeSinceStart('cpu'); if ($cpuTime !== null) { $this->mOutput->setLimitReportData('limitreport-cputime', sprintf("%.3f", $cpuTime)); } $wallTime = $this->mOutput->getTimeSinceStart('wall'); $this->mOutput->setLimitReportData('limitreport-walltime', sprintf("%.3f", $wallTime)); $this->mOutput->setLimitReportData('limitreport-ppvisitednodes', array($this->mPPNodeCount, $this->mOptions->getMaxPPNodeCount())); $this->mOutput->setLimitReportData('limitreport-ppgeneratednodes', array($this->mGeneratedPPNodeCount, $this->mOptions->getMaxGeneratedPPNodeCount())); $this->mOutput->setLimitReportData('limitreport-postexpandincludesize', array($this->mIncludeSizes['post-expand'], $max)); $this->mOutput->setLimitReportData('limitreport-templateargumentsize', array($this->mIncludeSizes['arg'], $max)); $this->mOutput->setLimitReportData('limitreport-expansiondepth', array($this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth())); $this->mOutput->setLimitReportData('limitreport-expensivefunctioncount', array($this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit())); Hooks::run('ParserLimitReportPrepare', array($this, $this->mOutput)); $limitReport = "NewPP limit report\n"; if ($wgShowHostnames) { $limitReport .= 'Parsed by ' . wfHostname() . "\n"; } $limitReport .= 'Cached time: ' . $this->mOutput->getCacheTime() . "\n"; $limitReport .= 'Cache expiry: ' . $this->mOutput->getCacheExpiry() . "\n"; $limitReport .= 'Dynamic content: ' . ($this->mOutput->hasDynamicContent() ? 'true' : 'false') . "\n"; foreach ($this->mOutput->getLimitReportData() as $key => $value) { if (Hooks::run('ParserLimitReportFormat', array($key, &$value, &$limitReport, false, false))) { $keyMsg = wfMessage($key)->inLanguage('en')->useDatabase(false); $valueMsg = wfMessage(array("{$key}-value-text", "{$key}-value"))->inLanguage('en')->useDatabase(false); if (!$valueMsg->exists()) { $valueMsg = new RawMessage('$1'); } if (!$keyMsg->isDisabled() && !$valueMsg->isDisabled()) { $valueMsg->params($value); $limitReport .= "{$keyMsg->text()}: {$valueMsg->text()}\n"; } } } // Since we're not really outputting HTML, decode the entities and // then re-encode the things that need hiding inside HTML comments. $limitReport = htmlspecialchars_decode($limitReport); Hooks::run('ParserLimitReport', array($this, &$limitReport)); // Sanitize for comment. Note '‐' in the replacement is U+2010, // which looks much like the problematic '-'. $limitReport = str_replace(array('-', '&'), array('‐', '&'), $limitReport); $text .= "\n<!-- \n{$limitReport}-->\n"; // Add on template profiling data $dataByFunc = $this->mProfiler->getFunctionStats(); uasort($dataByFunc, function ($a, $b) { return $a['real'] < $b['real']; // descending order }); $profileReport = "Transclusion expansion time report (%,ms,calls,template)\n"; foreach (array_slice($dataByFunc, 0, 10) as $item) { $profileReport .= sprintf("%6.2f%% %8.3f %6d - %s\n", $item['%real'], $item['real'], $item['calls'], htmlspecialchars($item['name'])); } $text .= "\n<!-- \n{$profileReport}-->\n"; if ($this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10) { wfDebugLog('generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' . $this->mTitle->getPrefixedDBkey()); } } $this->mOutput->setText($text); $this->mRevisionId = $oldRevisionId; $this->mRevisionObject = $oldRevisionObject; $this->mRevisionTimestamp = $oldRevisionTimestamp; $this->mRevisionUser = $oldRevisionUser; $this->mRevisionSize = $oldRevisionSize; $this->mInputSize = false; $this->currentRevisionCache = null; return $this->mOutput; }
/** * Convert wikitext to HTML * Do not call this function recursively. * * @param string $text Text we want to parse * @param Title $title * @param ParserOptions $options * @param bool $linestart * @param bool $clearState * @param int $revid Number to pass in {{REVISIONID}} * @return ParserOutput A ParserOutput */ public function parse($text, Title $title, ParserOptions $options, $linestart = true, $clearState = true, $revid = null) { /** * First pass--just handle <nowiki> sections, pass the rest off * to internalParse() which does all the real work. */ global $wgShowHostnames; if ($clearState) { // We use U+007F DELETE to construct strip markers, so we have to make // sure that this character does not occur in the input text. $text = strtr($text, "", "?"); $magicScopeVariable = $this->lock(); } $this->startParse($title, $options, self::OT_HTML, $clearState); $this->currentRevisionCache = null; $this->mInputSize = strlen($text); if ($this->mOptions->getEnableLimitReport()) { $this->mOutput->resetParseStartTime(); } $oldRevisionId = $this->mRevisionId; $oldRevisionObject = $this->mRevisionObject; $oldRevisionTimestamp = $this->mRevisionTimestamp; $oldRevisionUser = $this->mRevisionUser; $oldRevisionSize = $this->mRevisionSize; if ($revid !== null) { $this->mRevisionId = $revid; $this->mRevisionObject = null; $this->mRevisionTimestamp = null; $this->mRevisionUser = null; $this->mRevisionSize = null; } Hooks::run('ParserBeforeStrip', [&$this, &$text, &$this->mStripState]); # No more strip! Hooks::run('ParserAfterStrip', [&$this, &$text, &$this->mStripState]); $text = $this->internalParse($text); Hooks::run('ParserAfterParse', [&$this, &$text, &$this->mStripState]); $text = $this->internalParseHalfParsed($text, true, $linestart); /** * A converted title will be provided in the output object if title and * content conversion are enabled, the article text does not contain * a conversion-suppressing double-underscore tag, and no * {{DISPLAYTITLE:...}} is present. DISPLAYTITLE takes precedence over * automatic link conversion. */ if (!($options->getDisableTitleConversion() || isset($this->mDoubleUnderscores['nocontentconvert']) || isset($this->mDoubleUnderscores['notitleconvert']) || $this->mOutput->getDisplayTitle() !== false)) { $convruletitle = $this->getConverterLanguage()->getConvRuleTitle(); if ($convruletitle) { $this->mOutput->setTitleText($convruletitle); } else { $titleText = $this->getConverterLanguage()->convertTitle($title); $this->mOutput->setTitleText($titleText); } } # Done parsing! Compute runtime adaptive expiry if set $this->mOutput->finalizeAdaptiveCacheExpiry(); # Warn if too many heavyweight parser functions were used if ($this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit()) { $this->limitationWarn('expensive-parserfunction', $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit()); } # Information on include size limits, for the benefit of users who try to skirt them if ($this->mOptions->getEnableLimitReport()) { $max = $this->mOptions->getMaxIncludeSize(); $cpuTime = $this->mOutput->getTimeSinceStart('cpu'); if ($cpuTime !== null) { $this->mOutput->setLimitReportData('limitreport-cputime', sprintf("%.3f", $cpuTime)); } $wallTime = $this->mOutput->getTimeSinceStart('wall'); $this->mOutput->setLimitReportData('limitreport-walltime', sprintf("%.3f", $wallTime)); $this->mOutput->setLimitReportData('limitreport-ppvisitednodes', [$this->mPPNodeCount, $this->mOptions->getMaxPPNodeCount()]); $this->mOutput->setLimitReportData('limitreport-ppgeneratednodes', [$this->mGeneratedPPNodeCount, $this->mOptions->getMaxGeneratedPPNodeCount()]); $this->mOutput->setLimitReportData('limitreport-postexpandincludesize', [$this->mIncludeSizes['post-expand'], $max]); $this->mOutput->setLimitReportData('limitreport-templateargumentsize', [$this->mIncludeSizes['arg'], $max]); $this->mOutput->setLimitReportData('limitreport-expansiondepth', [$this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth()]); $this->mOutput->setLimitReportData('limitreport-expensivefunctioncount', [$this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit()]); Hooks::run('ParserLimitReportPrepare', [$this, $this->mOutput]); $limitReport = ''; Hooks::run('ParserLimitReport', [$this, &$limitReport]); if ($limitReport != '') { // Sanitize for comment. Note '‐' in the replacement is U+2010, // which looks much like the problematic '-'. $limitReport = str_replace(['-', '&'], ['‐', '&'], $limitReport); $text .= "\n<!-- \nNewPP limit report\n{$limitReport}-->\n"; } // Add on template profiling data in human/machine readable way $dataByFunc = $this->mProfiler->getFunctionStats(); uasort($dataByFunc, function ($a, $b) { return $a['real'] < $b['real']; // descending order }); $profileReport = []; foreach (array_slice($dataByFunc, 0, 10) as $item) { $profileReport[] = sprintf("%6.2f%% %8.3f %6d %s", $item['%real'], $item['real'], $item['calls'], $item['name']); } $this->mOutput->setLimitReportData('limitreport-timingprofile', $profileReport); // Add other cache related metadata if ($wgShowHostnames) { $this->mOutput->setLimitReportData('cachereport-origin', wfHostname()); } $this->mOutput->setLimitReportData('cachereport-timestamp', $this->mOutput->getCacheTime()); $this->mOutput->setLimitReportData('cachereport-ttl', $this->mOutput->getCacheExpiry()); $this->mOutput->setLimitReportData('cachereport-transientcontent', $this->mOutput->hasDynamicContent()); if ($this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10) { wfDebugLog('generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' . $this->mTitle->getPrefixedDBkey()); } } $this->mOutput->setText($text); $this->mRevisionId = $oldRevisionId; $this->mRevisionObject = $oldRevisionObject; $this->mRevisionTimestamp = $oldRevisionTimestamp; $this->mRevisionUser = $oldRevisionUser; $this->mRevisionSize = $oldRevisionSize; $this->mInputSize = false; $this->currentRevisionCache = null; return $this->mOutput; }
public function getFunctionStats() { return $this->sprofiler->getFunctionStats(); }