const TOC_START = '<mw:toc>';
const TOC_END = '</mw:toc>';
+ /** @var int Assume that no output will later be saved this many seconds after parsing */
+ const MAX_TTS = 900;
+
# Persistent:
public $mTagHooks = [];
public $mTransparentTagHooks = [];
/**
* @throws MWException
* @param array $m
- * @return HTML|string
+ * @return string HTML
*/
public function magicLinkCallback( $m ) {
if ( isset( $m[1] ) && $m[1] !== '' ) {
public function doHeadings( $text ) {
for ( $i = 6; $i >= 1; --$i ) {
$h = str_repeat( '=', $i );
- $text = preg_replace( "/^$h(.+)$h\\s*$/m", "<h$i>\\1</h$i>", $text );
+ // Trim non-newline whitespace from headings
+ // Using \s* will break for: "==\n===\n" and parse as <h2>=</h2>
+ $text = preg_replace( "/^(?:$h)[ \\t]*(.+?)[ \\t]*(?:$h)\\s*$/m", "<h$i>\\1</h$i>", $text );
}
return $text;
}
}
break;
case 'revisionday':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONDAY}} used, setting vary-revision...\n" );
- $value = intval( substr( $this->getRevisionTimestamp(), 6, 2 ) );
+ $value = (int)$this->getRevisionTimestampSubstring( 6, 2, self::MAX_TTS, $index );
break;
case 'revisionday2':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONDAY2}} used, setting vary-revision...\n" );
- $value = substr( $this->getRevisionTimestamp(), 6, 2 );
+ $value = $this->getRevisionTimestampSubstring( 6, 2, self::MAX_TTS, $index );
break;
case 'revisionmonth':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONMONTH}} used, setting vary-revision...\n" );
- $value = substr( $this->getRevisionTimestamp(), 4, 2 );
+ $value = $this->getRevisionTimestampSubstring( 4, 2, self::MAX_TTS, $index );
break;
case 'revisionmonth1':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONMONTH1}} used, setting vary-revision...\n" );
- $value = intval( substr( $this->getRevisionTimestamp(), 4, 2 ) );
+ $value = (int)$this->getRevisionTimestampSubstring( 4, 2, self::MAX_TTS, $index );
break;
case 'revisionyear':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONYEAR}} used, setting vary-revision...\n" );
- $value = substr( $this->getRevisionTimestamp(), 0, 4 );
+ $value = $this->getRevisionTimestampSubstring( 0, 4, self::MAX_TTS, $index );
break;
case 'revisiontimestamp':
# Let the edit saving system know we should parse the page
return $value;
}
+ /**
+ * @param int $start
+ * @param int $len
+ * @param int $mtts Max time-till-save; sets vary-revision if result might change by then
+ * @param string $variable Parser variable name
+ * @return string
+ */
+ private function getRevisionTimestampSubstring( $start, $len, $mtts, $variable ) {
+ global $wgContLang;
+
+ # Get the timezone-adjusted timestamp to be used for this revision
+ $resNow = substr( $this->getRevisionTimestamp(), $start, $len );
+ # Possibly set vary-revision if there is not yet an associated revision
+ if ( !$this->getRevisionObject() ) {
+ # Get the timezone-adjusted timestamp $mtts seconds in the future
+ $resThen = substr(
+ $wgContLang->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
+ $start,
+ $len
+ );
+
+ if ( $resNow !== $resThen ) {
+ # Let the edit saving system know we should parse the page
+ # *after* a revision ID has been assigned. This is for null edits.
+ $this->mOutput->setFlag( 'vary-revision' );
+ wfDebug( __METHOD__ . ": $variable used, setting vary-revision...\n" );
+ }
+ }
+
+ return $resNow;
+ }
+
/**
* initialise the magic variables (like CURRENTMONTHNAME) and substitution modifiers
*
* 'expansion-depth-exceeded-category')
* @param string|int|null $current Current value
* @param string|int|null $max Maximum allowed, when an explicit limit has been
- * exceeded, provide the values (optional)
+ * exceeded, provide the values (optional)
*/
public function limitationWarn( $limitationType, $current = '', $max = '' ) {
# does no harm if $current and $max are present but are unnecessary for the message
for ( $i = 0; $i < $argsLength; $i++ ) {
$funcArgs[] = $args->item( $i );
}
- try {
- $result = $this->callParserFunction( $frame, $func, $funcArgs );
- } catch ( Exception $ex ) {
- throw $ex;
- }
+
+ $result = $this->callParserFunction( $frame, $func, $funcArgs );
// Extract any forwarded flags
if ( isset( $result['title'] ) ) {
* @param Title $title
* @param array $options Array of options to RepoGroup::findFile
* @return File|bool
+ * @deprecated since 1.32, use fetchFileAndTitle instead
*/
public function fetchFile( $title, $options = [] ) {
+ wfDeprecated( __METHOD__, '1.32' );
return $this->fetchFileAndTitle( $title, $options )[0];
}
# Get all headlines for numbering them and adding funky stuff like [edit]
# links - this is for later, but we need the number of headlines right now
- # This regexp also trims whitespace in the heading's content
+ # NOTE: white space in headings have been trimmed in doHeadings. They shouldn't
+ # be trimmed here since whitespace in HTML headings is significant.
$matches = [];
$numMatches = preg_match_all(
- '/<H(?P<level>[1-6])(?P<attrib>.*?>)\s*(?P<header>[\s\S]*?)\s*<\/H[1-6] *>/i',
+ '/<H(?P<level>[1-6])(?P<attrib>.*?>)(?P<header>[\s\S]*?)<\/H[1-6] *>/i',
$text,
$matches
);
} else {
// Guess not, consider it as caption.
wfDebug( "$parameterMatch failed parameter validation\n" );
- $label = '|' . $parameterMatch;
+ $label = $parameterMatch;
}
}
} else {
// Last pipe wins.
- $label = '|' . $parameterMatch;
+ $label = $parameterMatch;
}
}
- // Remove the pipe.
- $label = substr( $label, 1 );
}
$ig->add( $title, $label, $alt, $link, $handlerOptions );