self::EXT_LINK_URL_CLASS . '*)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F\\x{FFFD}]*?)\]/Su';
if ( isset( $conf['preprocessorClass'] ) ) {
$this->mPreprocessorClass = $conf['preprocessorClass'];
- } elseif ( defined( 'HPHP_VERSION' ) ) {
- # Preprocessor_Hash is much faster than Preprocessor_DOM under HipHop
+ } elseif ( wfIsHHVM() ) {
+ # Under HHVM Preprocessor_Hash is much faster than Preprocessor_DOM
$this->mPreprocessorClass = Preprocessor_Hash::class;
} elseif ( extension_loaded( 'domxml' ) ) {
# PECL extension that conflicts with the core DOM extension (T15770)
* Do not call this function recursively.
*
* @param string $text Text we want to parse
+ * @param-taint $text escapes_htmlnoent
* @param Title $title
* @param ParserOptions $options
* @param bool $linestart
* @param bool $clearState
* @param int|null $revid Number to pass in {{REVISIONID}}
* @return ParserOutput A ParserOutput
+ * @return-taint escaped
*/
public function parse(
$text, Title $title, ParserOptions $options,
* $text are not expanded
*
* @param string $text Text extension wants to have parsed
+ * @param-taint $text escapes_htmlnoent
* @param bool|PPFrame $frame The frame to use for expanding any template variables
* @return string UNSAFE half-parsed HTML
+ * @return-taint escaped
*/
public function recursiveTagParse( $text, $frame = false ) {
// Avoid PHP 7.1 warning from passing $this by reference
* @since 1.25
*
* @param string $text Text extension wants to have parsed
+ * @param-taint $text escapes_htmlnoent
* @param bool|PPFrame $frame The frame to use for expanding any template variables
* @return string Fully parsed HTML
+ * @return-taint escaped
*/
public function recursiveTagParseFully( $text, $frame = false ) {
$text = $this->recursiveTagParse( $text, $frame );
* @private
*
* @param string $text The text to parse
+ * @param-taint $text escapes_html
* @param bool $isMain Whether this is being called from the main parse() function
* @param PPFrame|bool $frame A pre-processor frame
*
$this->mOutput->setFlag( 'vary-revision-id' );
wfDebug( __METHOD__ . ": {{REVISIONID}} used, setting vary-revision-id...\n" );
$value = $this->mRevisionId;
- if ( !$value && $this->mOptions->getSpeculativeRevIdCallback() ) {
- $value = call_user_func( $this->mOptions->getSpeculativeRevIdCallback() );
- $this->mOutput->setSpeculativeRevIdUsed( $value );
+
+ if ( !$value ) {
+ $rev = $this->getRevisionObject();
+ if ( $rev ) {
+ $value = $rev->getId();
+ }
+ }
+
+ if ( !$value ) {
+ $value = $this->mOptions->getSpeculativeRevId();
+ if ( $value ) {
+ $this->mOutput->setSpeculativeRevIdUsed( $value );
+ }
}
break;
case 'revisionday':
break;
case 'gallery-internal-link':
$linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) );
- $chars = self::EXT_LINK_URL_CLASS;
- $addr = self::EXT_LINK_ADDR;
- $prots = $this->mUrlProtocols;
- // check to see if link matches an absolute url, if not then it must be a wiki link.
if ( preg_match( '/^-{R|(.*)}-$/', $linkValue ) ) {
// Result of LanguageConverter::markNoConversion
// invoked on an external link.
$linkValue = substr( $linkValue, 4, -2 );
}
- if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) {
- $link = $linkValue;
- $this->mOutput->addExternalLink( $link );
- } else {
- $localLinkTitle = Title::newFromText( $linkValue );
- if ( $localLinkTitle !== null ) {
- $this->mOutput->addLink( $localLinkTitle );
- $link = $localLinkTitle->getLinkURL();
- }
+ list( $type, $target ) = $this->parseLinkParameter( $linkValue );
+ if ( $type === 'link-url' ) {
+ $link = $target;
+ $this->mOutput->addExternalLink( $target );
+ } elseif ( $type === 'link-title' ) {
+ $link = $target->getLinkURL();
+ $this->mOutput->addLink( $target );
}
break;
default:
$value = $this->stripAltText( $value, $holders );
break;
case 'link':
- $chars = self::EXT_LINK_URL_CLASS;
- $addr = self::EXT_LINK_ADDR;
- $prots = $this->mUrlProtocols;
- if ( $value === '' ) {
- $paramName = 'no-link';
- $value = true;
+ list( $paramName, $value ) = $this->parseLinkParameter( $value );
+ if ( $paramName ) {
$validated = true;
- } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) {
- if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) {
- $paramName = 'link-url';
- $this->mOutput->addExternalLink( $value );
+ if ( $paramName === 'no-link' ) {
+ $value = true;
+ }
+ if ( $paramName === 'link-url' ) {
if ( $this->mOptions->getExternalLinkTarget() ) {
$params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget();
}
- $validated = true;
- }
- } else {
- $linkTitle = Title::newFromText( $value );
- if ( $linkTitle ) {
- $paramName = 'link-title';
- $value = $linkTitle;
- $this->mOutput->addLink( $linkTitle );
- $validated = true;
}
}
break;
return $ret;
}
+ /**
+ * Parse the value of 'link' parameter in image syntax (`[[File:Foo.jpg|link=<value>]]`).
+ *
+ * Adds an entry to appropriate link tables.
+ *
+ * @since 1.32
+ * @return array of `[ type, target ]`, where:
+ * - `type` is one of:
+ * - `null`: Given value is not a valid link target, use default
+ * - `'no-link'`: Given value is empty, do not generate a link
+ * - `'link-url'`: Given value is a valid external link
+ * - `'link-title'`: Given value is a valid internal link
+ * - `target` is:
+ * - When `type` is `null` or `'no-link'`: `false`
+ * - When `type` is `'link-url'`: URL string corresponding to given value
+ * - When `type` is `'link-title'`: Title object corresponding to given value
+ */
+ public function parseLinkParameter( $value ) {
+ $chars = self::EXT_LINK_URL_CLASS;
+ $addr = self::EXT_LINK_ADDR;
+ $prots = $this->mUrlProtocols;
+ $type = null;
+ $target = false;
+ if ( $value === '' ) {
+ $type = 'no-link';
+ } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) {
+ if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) {
+ $this->mOutput->addExternalLink( $value );
+ $type = 'link-url';
+ $target = $value;
+ }
+ } else {
+ $linkTitle = Title::newFromText( $value );
+ if ( $linkTitle ) {
+ $this->mOutput->addLink( $linkTitle );
+ $type = 'link-title';
+ $target = $linkTitle;
+ }
+ }
+ return [ $type, $target ];
+ }
+
/**
* @param string $caption
* @param LinkHolderArray|bool $holders
if ( !is_null( $this->mRevisionObject ) ) {
return $this->mRevisionObject;
}
- if ( is_null( $this->mRevisionId ) ) {
- return null;
- }
+ // NOTE: try to get the RevisionObject even if mRevisionId is null.
+ // This is useful when parsing revision that has not yet been saved.
+ // However, if we get back a saved revision even though we are in
+ // preview mode, we'll have to ignore it, see below.
+ // NOTE: This callback may be used to inject an OLD revision that was
+ // already loaded, so "current" is a bit of a misnomer. We can't just
+ // skip it if mRevisionId is set.
$rev = call_user_func(
$this->mOptions->getCurrentRevisionCallback(), $this->getTitle(), $this
);
- # If the parse is for a new revision, then the callback should have
- # already been set to force the object and should match mRevisionId.
- # If not, try to fetch by mRevisionId for sanity.
- if ( $rev && $rev->getId() != $this->mRevisionId ) {
+ if ( $this->mRevisionId === null && $rev && $rev->getId() ) {
+ // We are in preview mode (mRevisionId is null), and the current revision callback
+ // returned an existing revision. Ignore it and return null, it's probably the page's
+ // current revision, which is not what we want here. Note that we do want to call the
+ // callback to allow the unsaved revision to be injected here, e.g. for
+ // self-transclusion previews.
+ return null;
+ }
+
+ // If the parse is for a new revision, then the callback should have
+ // already been set to force the object and should match mRevisionId.
+ // If not, try to fetch by mRevisionId for sanity.
+ if ( $this->mRevisionId && $rev && $rev->getId() != $this->mRevisionId ) {
$rev = Revision::newFromId( $this->mRevisionId );
}