/** * @covers Message::__construct * @covers Message::params */ public function testDeliciouslyManyParams() { $msg = new RawMessage('$1$2$3$4$5$6$7$8$9$10$11$12'); // One less than above has placeholders $params = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'); $this->assertEquals('abcdefghijka2', $msg->params($params)->plain(), 'Params > 9 are replaced correctly'); }
/** * Process the wikitext for the "?preload=" feature. (bug 5210) * * "<noinclude>", "<includeonly>" etc. are parsed as for template * transclusion, comments, templates, arguments, tags hooks and parser * functions are untouched. * * @param $text String * @param $title Title * @param $options ParserOptions * @param $params Array * @return String */ public function getPreloadText($text, Title $title, ParserOptions $options, $params = array()) { $msg = new RawMessage($text); $text = $msg->params($params)->plain(); # Parser (re)initialisation $this->startParse($title, $options, self::OT_PLAIN, true); $flags = PPFrame::NO_ARGS | PPFrame::NO_TEMPLATES; $dom = $this->preprocessToDom($text, self::PTD_FOR_INCLUSION); $text = $this->getPreprocessor()->newFrame()->expand($dom, $flags); $text = $this->mStripState->unstripBoth($text); return $text; }
/** * Get the error list as a Message object * * @param string|string[] $shortContext A short enclosing context message name (or an array of * message names), to be used when there is a single error. * @param string|string[] $longContext A long enclosing context message name (or an array of * message names), for a list. * * @return Message */ public function getMessage($shortContext = false, $longContext = false) { $rawErrors = $this->sv->getErrors(); if (count($rawErrors) == 0) { if ($this->sv->isOK()) { $this->sv->fatal('internalerror_info', __METHOD__ . " called for a good result, this is incorrect\n"); } else { $this->sv->fatal('internalerror_info', __METHOD__ . ": Invalid result object: no error text but not OK\n"); } $rawErrors = $this->sv->getErrors(); // just added a fatal } if (count($rawErrors) == 1) { $s = $this->getErrorMessage($rawErrors[0]); if ($shortContext) { $s = wfMessage($shortContext, $s); } elseif ($longContext) { $wrapper = new RawMessage("* \$1\n"); $wrapper->params($s)->parse(); $s = wfMessage($longContext, $wrapper); } } else { $msgs = $this->getErrorMessageArray($rawErrors); $msgCount = count($msgs); if ($shortContext) { $msgCount++; } $s = new RawMessage('* $' . implode("\n* \$", range(1, $msgCount))); $s->params($msgs)->parse(); if ($longContext) { $s = wfMessage($longContext, $s); } elseif ($shortContext) { $wrapper = new RawMessage("\n\$1\n", $s); $wrapper->parse(); $s = wfMessage($shortContext, $wrapper); } } return $s; }
/** * Get the Limit report for page previews * * @since 1.22 * @param ParserOutput $output ParserOutput object from the parse * @return string HTML */ public static function getPreviewLimitReport($output) { if (!$output || !$output->getLimitReportData()) { return ''; } wfProfileIn(__METHOD__); $limitReport = Html::rawElement('div', array('class' => 'mw-limitReportExplanation'), wfMessage('limitreport-title')->parseAsBlock()); // Show/hide animation doesn't work correctly on a table, so wrap it in a div. $limitReport .= Html::openElement('div', array('class' => 'preview-limit-report-wrapper')); $limitReport .= Html::openElement('table', array('class' => 'preview-limit-report wikitable')) . Html::openElement('tbody'); foreach ($output->getLimitReportData() as $key => $value) { if (wfRunHooks('ParserLimitReportFormat', array($key, &$value, &$limitReport, true, true))) { $keyMsg = wfMessage($key); $valueMsg = wfMessage(array("{$key}-value-html", "{$key}-value")); if (!$valueMsg->exists()) { $valueMsg = new RawMessage('$1'); } if (!$keyMsg->isDisabled() && !$valueMsg->isDisabled()) { $limitReport .= Html::openElement('tr') . Html::rawElement('th', null, $keyMsg->parse()) . Html::rawElement('td', null, $valueMsg->params($value)->parse()) . Html::closeElement('tr'); } } } $limitReport .= Html::closeElement('tbody') . Html::closeElement('table') . Html::closeElement('div'); wfProfileOut(__METHOD__); return $limitReport; }
/** * Get the Limit report for page previews * * @since 1.22 * @param ParserOutput $output ParserOutput object from the parse * @return string HTML */ public static function getPreviewLimitReport($output) { if (!$output || !$output->getLimitReportData()) { return ''; } $limitReport = Html::rawElement('div', ['class' => 'mw-limitReportExplanation'], wfMessage('limitreport-title')->parseAsBlock()); // Show/hide animation doesn't work correctly on a table, so wrap it in a div. $limitReport .= Html::openElement('div', ['class' => 'preview-limit-report-wrapper']); $limitReport .= Html::openElement('table', ['class' => 'preview-limit-report wikitable']) . Html::openElement('tbody'); foreach ($output->getLimitReportData()['limitreport'] as $key => $value) { if (Hooks::run('ParserLimitReportFormat', [$key, &$value, &$limitReport, true, true])) { $keyMsg = wfMessage("limitreport-{$key}"); $valueMsg = wfMessage(["limitreport-{$key}-value-html", "limitreport-{$key}-value"]); if (!$valueMsg->exists()) { $valueMsg = new RawMessage('$1'); } if (!$keyMsg->isDisabled() && !$valueMsg->isDisabled()) { // If it's a value/limit array, convert it for $1/$2 if (is_array($value) && isset($value['value'])) { $value = [$value['value'], $value['limit']]; } $limitReport .= Html::openElement('tr') . Html::rawElement('th', null, $keyMsg->parse()) . Html::rawElement('td', null, $valueMsg->params($value)->parse()) . Html::closeElement('tr'); } } } $limitReport .= Html::closeElement('tbody') . Html::closeElement('table') . Html::closeElement('div'); return $limitReport; }
/** * @covers Message::params * @covers Message::toString * @covers Message::replaceParameters */ public function testReplaceManyParams() { $msg = new RawMessage('$1$2$3$4$5$6$7$8$9$10$11$12'); // One less than above has placeholders $params = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']; $this->assertEquals('abcdefghijka2', $msg->params($params)->plain(), 'Params > 9 are replaced correctly'); $msg = new RawMessage('Params$*'); $params = ['ab', 'bc', 'cd']; $this->assertEquals('Params: ab, bc, cd', $msg->params($params)->text()); }
/** * Convert wikitext to HTML * Do not call this function recursively. * * @param string $text text we want to parse * @param $title Title object * @param $options ParserOptions * @param $linestart boolean * @param $clearState boolean * @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 $wgUseTidy, $wgAlwaysUseTidy, $wgShowHostnames; $fname = __METHOD__ . '-' . wfGetCaller(); wfProfileIn( __METHOD__ ); wfProfileIn( $fname ); $this->startParse( $title, $options, self::OT_HTML, $clearState ); $this->mInputSize = strlen( $text ); if ( $this->mOptions->getEnableLimitReport() ) { $this->mOutput->resetParseStartTime(); } # Remove the strip marker tag prefix from the input, if present. if ( $clearState ) { $text = str_replace( $this->mUniqPrefix, '', $text ); } $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; } wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) ); # No more strip! wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) ); $text = $this->internalParse( $text ); wfRunHooks( 'ParserAfterParse', array( &$this, &$text, &$this->mStripState ) ); $text = $this->mStripState->unstripGeneral( $text ); # Clean up special characters, only run once, next-to-last before doBlockLevels $fixtags = array( # french spaces, last one Guillemet-left # only if there is something before the space '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 ', # french spaces, Guillemet-right '/(\\302\\253) /' => '\\1 ', '/ (!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874. ); $text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text ); $text = $this->doBlockLevels( $text, $linestart ); $this->replaceLinkHolders( $text ); /** * The input doesn't get language converted if * a) It's disabled * b) Content isn't converted * c) It's a conversion table * d) it is an interface message (which is in the user language) */ if ( !( $options->getDisableContentConversion() || isset( $this->mDoubleUnderscores['nocontentconvert'] ) ) ) { if ( !$this->mOptions->getInterfaceMessage() ) { # The position of the convert() call should not be changed. it # assumes that the links are all replaced and the only thing left # is the <nowiki> mark. $text = $this->getConverterLanguage()->convert( $text ); } } /** * 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 ); } } $text = $this->mStripState->unstripNoWiki( $text ); wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) ); $text = $this->replaceTransparentTags( $text ); $text = $this->mStripState->unstripGeneral( $text ); $text = Sanitizer::normalizeCharReferences( $text ); if ( ( $wgUseTidy && $this->mOptions->getTidy() ) || $wgAlwaysUseTidy ) { $text = MWTidy::tidy( $text ); } else { # attempt to sanitize at least some nesting problems # (bug #2702 and quite a few others) $tidyregs = array( # ''Something [http://www.cool.com cool''] --> # <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a> '/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' => '\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9', # fix up an anchor inside another anchor, only # at least for a single single nested link (bug 3695) '/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' => '\\1\\2</a>\\3</a>\\1\\4</a>', # fix div inside inline elements- doBlockLevels won't wrap a line which # contains a div, so fix it up here; replace # div with escaped text '/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\/div>)([^<]*)(<\/\\2>)/' => '\\1\\3<div\\5>\\6</div>\\8\\9', # remove empty italic or bold tag pairs, some # introduced by rules above '/<([bi])><\/\\1>/' => '', ); $text = preg_replace( array_keys( $tidyregs ), array_values( $tidyregs ), $text ); } if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) { $this->limitationWarn( 'expensive-parserfunction', $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ); } wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) ); # 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() ) ); wfRunHooks( 'ParserLimitReportPrepare', array( $this, $this->mOutput ) ); $limitReport = "NewPP limit report\n"; if ( $wgShowHostnames ) { $limitReport .= 'Parsed by ' . wfHostname() . "\n"; } foreach ( $this->mOutput->getLimitReportData() as $key => $value ) { if ( wfRunHooks( '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 ); wfRunHooks( '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"; 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; wfProfileOut( $fname ); wfProfileOut( __METHOD__ ); return $this->mOutput; }
public static function from(RawMessage $message) { return new static($message->nickname(), $message->username(), $message->hostname(), $message->serverName(), $message->command(), $message->params()); }