* Globals used:
* objects: $wgLang, $wgContLang
*
- * NOT $wgArticle, $wgUser or $wgTitle. Keep them away!
+ * NOT $wgUser or $wgTitle. Keep them away!
*
* settings:
* $wgUseDynamicDates*, $wgInterwikiMagic*,
# Constants needed for external link processing
# Everything except bracket, space, or control characters
- const EXT_LINK_URL_CLASS = '(?:[^\]\[<>"\\x00-\\x20\\x7F]|(?:\[\]))';
- const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)([^][<>"\\x00-\\x20\\x7F]+)
- \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sx';
+ # \p{Zs} is unicode 'separator, space' category. It covers the space 0x20
+ # as well as U+3000 is IDEOGRAPHIC SPACE for bug 19052
+ const EXT_LINK_URL_CLASS = '[^][<>"\\x00-\\x20\\x7F\p{Zs}]';
+ const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)([^][<>"\\x00-\\x20\\x7F\p{Zs}]+)
+ \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sxu';
# State constants for the definition list colon extraction
const COLON_STATE_TEXT = 0;
var $mImageParamsMagicArray = array();
var $mMarkerIndex = 0;
var $mFirstCall = true;
- var $mVariables, $mSubstWords; # Initialised by initialiseVariables()
+
+ # Initialised by initialiseVariables()
+
+ /**
+ * @var MagicWordArray
+ */
+ var $mVariables;
+
+ /**
+ * @var MagicWordArray
+ */
+ var $mSubstWords;
var $mConf, $mPreprocessor, $mExtLinkBracketedRegex, $mUrlProtocols; # Initialised in constructor
# Cleared with clearState():
/**
- * @var OutputPage
+ * @var ParserOutput
*/
var $mOutput;
var $mAutonumber, $mDTopen;
var $mStripState;
var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
- var $mLinkHolders, $mLinkID;
+ /**
+ * @var LinkHolderArray
+ */
+ var $mLinkHolders;
+
+ var $mLinkID;
var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
var $mTplExpandCache; # empty-frame expansion cache
var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
var $mExpensiveFunctionCount; # number of expensive parser function calls
+ var $mShowToc, $mForceTocPosition;
/**
* @var User
var $mRevisionObject; # The revision object of the specified revision ID
var $mRevisionId; # ID to display in {{REVISIONID}} tags
var $mRevisionTimestamp; # The timestamp of the specified revision ID
- var $mRevisionUser; # Userto display in {{REVISIONUSER}} tag
+ var $mRevisionUser; # User to display in {{REVISIONUSER}} tag
var $mRevIdForTs; # The revision ID which was used to fetch the timestamp
+ /**
+ * @var string
+ */
+ var $mUniqPrefix;
+
/**
* Constructor
+ *
+ * @param $conf array
*/
public function __construct( $conf = array() ) {
$this->mConf = $conf;
$this->mUrlProtocols = wfUrlProtocols();
- $this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'.
- '(?:[^\]\[<>"\x00-\x20\x7F]|\[\])+) *([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/S';
+ $this->mExtLinkBracketedRegex = '/\[((' . wfUrlProtocols() . ')'.
+ self::EXT_LINK_URL_CLASS.'+)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/Su';
if ( isset( $conf['preprocessorClass'] ) ) {
$this->mPreprocessorClass = $conf['preprocessorClass'];
+ } elseif ( defined( 'MW_COMPILED' ) ) {
+ # Preprocessor_Hash is much faster than Preprocessor_DOM in compiled mode
+ $this->mPreprocessorClass = 'Preprocessor_Hash';
} elseif ( extension_loaded( 'domxml' ) ) {
# PECL extension that conflicts with the core DOM extension (bug 13770)
wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
} else {
$this->mPreprocessorClass = 'Preprocessor_Hash';
}
+ wfDebug( __CLASS__ . ": using preprocessor: {$this->mPreprocessorClass}\n" );
}
/**
*/
function __destruct() {
if ( isset( $this->mLinkHolders ) ) {
- $this->mLinkHolders->__destruct();
+ unset( $this->mLinkHolders );
}
foreach ( $this as $name => $value ) {
unset( $this->$name );
* Do not call this function recursively.
*
* @param $text String: text we want to parse
- * @param $title A title object
+ * @param $title Title object
* @param $options ParserOptions
* @param $linestart boolean
* @param $clearState boolean
$fixtags = array(
# french spaces, last one Guillemet-left
# only if there is something before the space
- '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 \\2',
+ '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 ',
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1 ',
'/ (!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
$this->replaceLinkHolders( $text );
/**
- * The page doesn't get language converted if
+ * 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 ( !( $wgDisableLangConversion
|| isset( $this->mDoubleUnderscores['nocontentconvert'] )
- || $this->mTitle->isConversionTable() ) ) {
+ || $this->mTitle->isConversionTable()
+ || $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
*
* @param $text String: text extension wants to have parsed
* @param $frame PPFrame: The frame to use for expanding any template variables
+ *
+ * @return string
*/
function recursiveTagParse( $text, $frame=false ) {
wfProfileIn( __METHOD__ );
return $text;
}
+ /**
+ * Recursive parser entry point that can be called from an extension tag
+ * hook.
+ *
+ * @param $text String: text to be expanded
+ * @param $frame PPFrame: The frame to use for expanding any template variables
+ * @return String
+ */
+ public function recursivePreprocess( $text, $frame = false ) {
+ wfProfileIn( __METHOD__ );
+ $text = $this->replaceVariables( $text, $frame );
+ $text = $this->mStripState->unstripBoth( $text );
+ wfProfileOut( __METHOD__ );
+ return $text;
+ }
+
/**
* Process the wikitext for the ?preload= feature. (bug 5210)
*
/**
* Get a random string
*
- * @static
+ * @return string
*/
static public function getRandomString() {
return dechex( mt_rand( 0, 0x7fffffff ) ) . dechex( mt_rand( 0, 0x7fffffff ) );
*/
public function uniqPrefix() {
if ( !isset( $this->mUniqPrefix ) ) {
- # @todo Fixme: this is probably *horribly wrong*
+ # @todo FIXME: This is probably *horribly wrong*
# LanguageConverter seems to want $wgParser's uniqPrefix, however
# if this is called for a parser cache hit, the parser may not
# have ever been initialized in the first place.
/**
* Set the context title
+ *
+ * @param $t Title
*/
function setTitle( $t ) {
if ( !$t || $t instanceof FakeTitle ) {
return wfSetVar( $this->mOptions, $x );
}
+ /**
+ * @return int
+ */
function nextLinkID() {
return $this->mLinkID++;
}
+ /**
+ * @param $id int
+ */
function setLinkID( $id ) {
$this->mLinkID = $id;
}
* @return Language
*/
function getFunctionLang() {
- global $wgLang, $wgContLang;
+ global $wgLang;
$target = $this->mOptions->getTargetLanguage();
if ( $target !== null ) {
return $target;
} else {
- return $this->mOptions->getInterfaceMessage() ? $wgLang : $wgContLang;
+ return $this->mOptions->getInterfaceMessage() ? $wgLang : $this->mTitle->getPageLanguage();
}
}
* array( 'param' => 'x' ),
* '<element param="x">tag content</element>' ) )
*
- * @param $elements list of element names. Comments are always extracted.
- * @param $text Source text string.
- * @param $matches Out parameter, Array: extracted tags
- * @param $uniq_prefix
+ * @param $elements array list of element names. Comments are always extracted.
+ * @param $text string Source text string.
+ * @param $matches array Out parameter, Array: extracted tags
+ * @param $uniq_prefix string
* @return String: stripped text
- *
- * @static
*/
- public function extractTagsAndParams( $elements, $text, &$matches, $uniq_prefix = '' ) {
+ public static function extractTagsAndParams( $elements, $text, &$matches, $uniq_prefix = '' ) {
static $n = 1;
$stripped = '';
$matches = array();
/**
* Get a list of strippable XML-like elements
+ *
+ * @return array
*/
function getStripList() {
return $this->mStripList;
}
- /**
- * @deprecated use replaceVariables
- */
- function strip( $text, $state, $stripcomments = false , $dontstrip = array() ) {
- return $text;
- }
-
- /**
- * Restores pre, math, and other extensions removed by strip()
- *
- * always call unstripNoWiki() after this one
- * @private
- * @deprecated use $this->mStripState->unstrip()
- */
- function unstrip( $text, $state ) {
- return $state->unstripGeneral( $text );
- }
-
- /**
- * Always call this after unstrip() to preserve the order
- *
- * @private
- * @deprecated use $this->mStripState->unstrip()
- */
- function unstripNoWiki( $text, $state ) {
- return $state->unstripNoWiki( $text );
- }
-
- /**
- * @deprecated use $this->mStripState->unstripBoth()
- */
- function unstripForHTML( $text ) {
- return $this->mStripState->unstripBoth( $text );
- }
-
/**
* Add an item to the strip state
* Returns the unique tag which must be inserted into the stripped text
* The tag will be replaced with the original text in unstrip()
*
- * @private
+ * @param $text string
+ *
+ * @return string
*/
function insertStripItem( $text ) {
$rnd = "{$this->mUniqPrefix}-item-{$this->mMarkerIndex}-" . self::MARKER_SUFFIX;
return $rnd;
}
- /**
- * Interface with html tidy
- * @deprecated Use MWTidy::tidy()
- */
- public static function tidy( $text ) {
- wfDeprecated( __METHOD__ );
- return MWTidy::tidy( $text );
- }
-
/**
* parse the wiki syntax used to render tables
*
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doTableStuff( $text ) {
wfProfileIn( __METHOD__ );
foreach ( $lines as $outLine ) {
$line = trim( $outLine );
- if ( $line === '' ) { // empty line, go to next line
- $out .= $outLine . "\n";
+ # empty line, go to next line,
+ # but only append \n if outside of table
+ if ( $line === '') {
+ $output .= $outLine . "\n";
continue;
}
$firstChars = $line[0];
$table =& $this->last( $tables );
$table[0] = array(); // first row
$currentRow =& $table[0];
-
$table['indent'] = strlen( $matches[1] );
$attributes = $this->mStripState->unstripBoth( $matches[2] );
if ( $attributes !== '' ) {
$table['attributes'] = $attributes;
}
- } else if ( !isset( $tables[0] ) ) {
+ } elseif ( !isset( $tables[0] ) ) {
// we're outside the table
$out .= $outLine . "\n";
- } else if ( $firstChars === '|}' ) {
+ } elseif ( $firstChars === '|}' ) {
// trim the |} code from the line
$line = substr ( $line , 2 );
if ( empty( $lastRow ) ) {
$lastRow = NULL;
}
+ $o = '';
$curtable = array_pop( $tables );
- $o = $this->generateTableHTML( $curtable ) . $line;
+
+ #Add a line-ending before the table, but only if there isn't one already
+ if ( substr( $out, -1 ) !== "\n" ) {
+ $o .= "\n";
+ }
+ $o .= $this->generateTableHTML( $curtable ) . $line . "\n";
if ( count( $tables ) > 0 ) {
$table =& $this->last( $tables );
$output .= $o;
- } else if ( $firstChars === '|-' ) {
+ } elseif ( $firstChars === '|-' ) {
// start a new row element
// but only when we haven't started one already
if ( count( $currentRow ) != 0 ) {
$currentRow['attributes'] = $attributes;
}
- } else if ( $firstChars === '|+' ) {
- // a table caption
- $line = substr ( $line , 2 );
-
- $c = $this->getCellAttr( $line , 'caption' );
- $table['caption'] = array();
- $table['caption']['content'] = $c[0];
- if ( isset( $c[1] ) ) $table['caption']['attributes'] = $c[1];
- unset( $c );
+ } elseif ( $firstChars === '|+' ) {
+ // a table caption, but only proceed if there isn't one already
+ if ( !isset ( $table['caption'] ) ) {
+ $line = substr ( $line , 2 );
- $output =& $table['caption'];
- } else if ( $firstChars === '|' || $firstChars === '!' || $firstChars === '!+' ) {
+ $c = $this->getCellAttr( $line , 'caption' );
+ $table['caption'] = array();
+ $table['caption']['content'] = $c[0];
+ if ( isset( $c[1] ) ) $table['caption']['attributes'] = $c[1];
+ unset( $c );
+ $output =& $table['caption']['content'];
+ }
+ } elseif ( $firstChars === '|' || $firstChars === '!' || $firstChars === '!+' ) {
// Which kind of cells are we dealing with
$currentTag = 'td';
$line = substr ( $line , 1 );
// decide whether thead to tbody
if ( !array_key_exists( 'type', $currentRow ) ) {
$currentRow['type'] = ( $firstChars === '!' ) ? 'thead' : 'tbody' ;
- } else if ( $firstChars === '|' ) {
+ } elseif ( $firstChars === '|' ) {
$currentRow['type'] = 'tbody';
}
$output =& $currentElement['content'];
} else {
- $output .= $outLine . "\n";
+ $output .= "\n$outLine";
}
}
if ( isset( $tables ) && count( $tables ) > 0 ) {
for ( $i = 0; $i < count( $tables ); $i++ ) {
$curtable = array_pop( $tables );
- $out .= $this->generateTableHTML( $curtable );
+ $curtable = $this->generateTableHTML( $curtable );
+ #Add a line-ending before the table, but only if there isn't one already
+ if ( substr( $out, -1 ) !== "\n" && $curtable !== "" ) {
+ $out .= "\n";
+ }
+ $out .= $curtable;
}
}
return $out;
}
-
/**
* Helper function for doTableStuff() separating the contents of cells from
- * attributes. Particularly useful as there's a possible bug and this action
+ * attributes. Particularly useful as there's a possible bug and this action
* is repeated twice.
*
* @private
+ * @param $cell
+ * @param $tagName
+ * @return array
*/
function getCellAttr ( $cell, $tagName ) {
- $content = null;
$attributes = null;
$cell = trim ( $cell );
if ( strpos( $cellData[0], '[[' ) !== false ) {
$content = trim ( $cell );
}
- else if ( count ( $cellData ) == 1 ) {
+ elseif ( count ( $cellData ) == 1 ) {
$content = trim ( $cellData[0] );
- }
- else {
+ } else {
$attributes = $this->mStripState->unstripBoth( $cellData[0] );
$attributes = Sanitizer::fixTagAttributes( $attributes , $tagName );
* Helper function for doTableStuff(). This converts the structured array into html.
*
* @private
+ *
+ * @param $table array
+ *
+ * @return string
*/
- function generateTableHTML ( &$table ) {
- $return = "\n";
- $return .= str_repeat( '<dl><dd>' , $table['indent'] );
+ function generateTableHTML( &$table ) {
+ $return = str_repeat( '<dl><dd>' , $table['indent'] );
$return .= '<table';
$return .= isset( $table['attributes'] ) ? $table['attributes'] : '';
$return .= '>';
$return .= isset( $table['caption']['attributes'] ) ? $table['caption']['attributes'] : '';
$return .= '>';
$return .= $table['caption']['content'];
- $return .= '</caption>';
+ $return .= "\n</caption>";
}
$lastSection = '';
$empty = true;
// If we only have tbodies, mark table as simple
for ( $i = 0; isset( $table[$i] ); $i++ ) {
if ( !count( $table[$i] ) ) continue;
+ if ( !isset( $table[$i]['type'] ) ) {
+ $table[$i]['type'] = 'tbody';
+ }
if ( !$lastSection ) {
$lastSection = $table[$i]['type'];
- } else if ( $lastSection != $table[$i]['type'] ) {
+ } elseif ( $lastSection != $table[$i]['type'] ) {
$simple = false;
- break;
}
}
$lastSection = '';
for ( $i = 0; isset( $table[$i] ); $i++ ) {
- // Check for empty tables
- if ( count( $table[$i] ) ) {
- $empty = false;
- } else {
- continue;
- }
+ if ( !count( $table[$i] ) ) continue;
+ $empty = false; // check for empty tables
+
if ( $table[$i]['type'] != $lastSection && !$simple ) {
$return .= "\n<" . $table[$i]['type'] . '>';
}
$return .= isset( $table[$i]['attributes'] ) ? $table[$i]['attributes'] : '';
$return .= '>';
for ( $j = 0; isset( $table[$i][$j] ); $j++ ) {
+ if ( !isset( $table[$i][$j]['type'] ) ) $table[$i][$j]['type'] = 'td';
$return .= "\n<" . $table[$i][$j]['type'];
$return .= isset( $table[$i][$j]['attributes'] ) ? $table[$i][$j]['attributes'] : '';
$return .= '>';
$return .= $table[$i][$j]['content'];
+ if ( $table[$i][$j]['content'] != '' )
+ $return .= "\n";
$return .= '</' . $table[$i][$j]['type'] . '>';
unset( $table[$i][$j] );
}
$return .= "\n</tr>";
- if ( ( !isset( $table[$i + 1] ) && !$simple ) || ( isset( $table[$i + 1] ) && ( $table[$i]['type'] != $table[$i + 1]['type'] ) ) ) {
+ if ( ( !isset( $table[$i + 1] ) && !$simple ) || ( isset( $table[$i + 1] ) && isset( $table[$i + 1]['type'] ) && $table[$i]['type'] != $table[$i + 1]['type'] ) ) {
$return .= '</' . $table[$i]['type'] . '>';
}
$lastSection = $table[$i]['type'];
* no numric elements and an array itself if not previously defined.
*
* @private
+ *
+ * @param $arr array
*/
function &last ( &$arr ) {
for ( $i = count( $arr ); ( !isset( $arr[$i] ) && $i > 0 ); $i-- ) { }
* HTML. Only called for $mOutputType == self::OT_HTML.
*
* @private
+ *
+ * @param $text string
+ * @param $isMain bool
+ * @param $frame bool
+ *
+ * @return string
*/
- function internalParse( $text, $isMain = true, $frame=false ) {
+ function internalParse( $text, $isMain = true, $frame = false ) {
wfProfileIn( __METHOD__ );
$origText = $text;
*
* DML
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doMagicLinks( $text ) {
wfProfileIn( __METHOD__ );
- $prots = $this->mUrlProtocols;
+ $prots = wfUrlProtocolsWithoutProtRel();
$urlChar = self::EXT_LINK_URL_CLASS;
$text = preg_replace_callback(
'!(?: # Start cases
(?: [0-9] [\ \-]? ){9} # 9 digits with opt. delimiters
[0-9Xx] # check digit
\b)
- )!x', array( &$this, 'magicLinkCallback' ), $text );
+ )!xu', array( &$this, 'magicLinkCallback' ), $text );
wfProfileOut( __METHOD__ );
return $text;
}
+ /**
+ * @throws MWException
+ * @param $m array
+ * @return HTML|string
+ */
function magicLinkCallback( $m ) {
if ( isset( $m[1] ) && $m[1] !== '' ) {
# Skip anchor
/**
* Make a free external link, given a user-supplied URL
- * @return HTML
+ *
+ * @param $url string
+ *
+ * @return string HTML
* @private
*/
function makeFreeExternalLink( $url ) {
* Parse headers and return html
*
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doHeadings( $text ) {
wfProfileIn( __METHOD__ );
/**
* Replace single quotes with HTML markup
* @private
+ *
+ * @param $text string
+ *
* @return string the altered text
*/
function doAllQuotes( $text ) {
/**
* Helper function for doAllQuotes()
+ *
+ * @param $text string
+ *
+ * @return string
*/
public function doQuotes( $text ) {
$arr = preg_split( "/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
* Make sure to run maintenance/parserTests.php if you change this code.
*
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function replaceExternalLinks( $text ) {
global $wgContLang;
# No link text, e.g. [http://domain.tld/some.link]
if ( $text == '' ) {
- # Autonumber if allowed. See bug #5918
- if ( strpos( wfUrlProtocols(), substr( $protocol, 0, strpos( $protocol, ':' ) ) ) !== false ) {
- $langObj = $this->getFunctionLang();
- $text = '[' . $langObj->formatNum( ++$this->mAutonumber ) . ']';
- $linktype = 'autonumber';
- } else {
- # Otherwise just use the URL
- $text = htmlspecialchars( $url );
- $linktype = 'free';
- }
+ # Autonumber
+ $langObj = $this->getFunctionLang();
+ $text = '[' . $langObj->formatNum( ++$this->mAutonumber ) . ']';
+ $linktype = 'autonumber';
} else {
# Have link text, e.g. [http://domain.tld/some.link text]s
# Check for trail
* (depending on configuration, namespace, and the URL's domain) and/or a
* target attribute (depending on configuration).
*
- * @param $url String: optional URL, to extract the domain from for rel =>
+ * @param $url String|bool optional URL, to extract the domain from for rel =>
* nofollow if appropriate
- * @return Array: associative array of HTML attributes
+ * @return Array associative array of HTML attributes
*/
function getExternalLinkAttribs( $url = false ) {
$attribs = array();
return $attribs;
}
-
/**
* Replace unusual URL escape codes with their equivalent characters
*
/**
* Callback function used in replaceUnusualEscapes().
* Replaces unusual URL escape codes with their equivalent character
+ *
+ * @param $matches array
+ *
+ * @return string
*/
private static function replaceUnusualEscapesCallback( $matches ) {
$char = urldecode( $matches[0] );
* make an image if it's allowed, either through the global
* option, through the exception, or through the on-wiki whitelist
* @private
+ *
+ * $param $url string
+ *
+ * @return string
*/
function maybeMakeExternalImage( $url ) {
$imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
/**
* Process [[ ]] wikilinks
+ *
+ * @param $s string
+ *
* @return String: processed text
*
* @private
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
# should be external links.
- if ( preg_match( '/^\b(?:' . wfUrlProtocols() . ')/', $m[1] ) ) {
+ if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $m[1] ) ) {
$s .= $prefix . '[[' . $line ;
wfProfileOut( __METHOD__."-misc" );
continue;
}
# NS_MEDIA is a pseudo-namespace for linking directly to a file
- # FIXME: Should do batch file existence checks, see comment below
+ # @todo FIXME: Should do batch file existence checks, see comment below
if ( $ns == NS_MEDIA ) {
wfProfileIn( __METHOD__."-media" );
# Give extensions a chance to select the file revision for us
# Some titles, such as valid special pages or files in foreign repos, should
# be shown as bluelinks even though they're not included in the page table
#
- # FIXME: isAlwaysKnown() can be expensive for file links; we should really do
+ # @todo FIXME: isAlwaysKnown() can be expensive for file links; we should really do
# batch file existence checks for NS_FILE and NS_MEDIA
if ( $iw == '' && $nt->isAlwaysKnown() ) {
$this->mOutput->addLink( $nt );
return $holders;
}
- /**
- * Make a link placeholder. The text returned can be later resolved to a real link with
- * replaceLinkHolders(). This is done for two reasons: firstly to avoid further
- * parsing of interwiki links, and secondly to allow all existence checks and
- * article length checks (for stub links) to be bundled into a single query.
- *
- * @deprecated
- */
- function makeLinkHolder( &$nt, $text = '', $query = array(), $trail = '', $prefix = '' ) {
- return $this->mLinkHolders->makeHolder( $nt, $text, $query, $trail, $prefix );
- }
-
/**
* Render a forced-blue link inline; protect against double expansion of
* URLs if we're in a mode that prepends full URL prefixes to internal links.
/**#@+
* Used by doBlockLevels()
* @private
+ *
+ * @return string
*/
function closeParagraph() {
$result = '';
* getCommon() returns the length of the longest common substring
* of both arguments, starting at the beginning of both.
* @private
+ *
+ * @param $st1 string
+ * @param $st2 string
+ *
+ * @return int
*/
function getCommon( $st1, $st2 ) {
$fl = strlen( $st1 );
}
for ( $i = 0; $i < $shorter; ++$i ) {
- if ( $st1{$i} != $st2{$i} ) {
+ if ( $st1[$i] != $st2[$i] ) {
break;
}
}
* These next three functions open, continue, and close the list
* element appropriate to the prefix character passed into them.
* @private
+ *
+ * @param $char char
+ *
+ * @return string
*/
function openList( $char ) {
$result = $this->closeParagraph();
* TODO: document
* @param $char String
* @private
+ *
+ * @return string
*/
function nextItem( $char ) {
if ( '*' === $char || '#' === $char ) {
* TODO: document
* @param $char String
* @private
+ *
+ * @return string
*/
function closeList( $char ) {
if ( '*' === $char ) {
$output .= $this->openList( $char );
if ( ';' === $char ) {
- # FIXME: This is dupe of code above
+ # @todo FIXME: This is dupe of code above
if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
$t = $t2;
$output .= $term . $this->nextItem( ':' );
* Split up a string on ':', ignoring any occurences inside tags
* to prevent illegal overlapping.
*
- * @param $str String: the string to split
- * @param &$before String: set to everything before the ':'
- * @param &$after String: set to everything after the ':'
- * return String: the position of the ':', or false if none found
+ * @param $str String the string to split
+ * @param &$before String set to everything before the ':'
+ * @param &$after String set to everything after the ':'
+ * @return String the position of the ':', or false if none found
*/
function findColonNoLinks( $str, &$before, &$after ) {
wfProfileIn( __METHOD__ );
$stack = 0;
$len = strlen( $str );
for( $i = 0; $i < $len; $i++ ) {
- $c = $str{$i};
+ $c = $str[$i];
switch( $state ) {
# (Using the number is a performance hack for common cases)
* Return value of a magic variable (like PAGENAME)
*
* @private
+ *
+ * @param $index integer
+ * @param $frame PPFrame
+ *
+ * @return string
*/
- function getVariableValue( $index, $frame=false ) {
+ function getVariableValue( $index, $frame = false ) {
global $wgContLang, $wgSitename, $wgServer;
global $wgArticlePath, $wgScriptPath, $wgStylePath;
+ if ( is_null( $this->mTitle ) ) {
+ // If no title set, bad things are going to happen
+ // later. Title should always be set since this
+ // should only be called in the middle of a parse
+ // operation (but the unit-tests do funky stuff)
+ throw new MWException( __METHOD__ . ' Should only be '
+ . ' called while parsing (no title set)' );
+ }
+
/**
* Some of these require message or data lookups and can be
* expensive to check many times.
date_default_timezone_set( $oldtz );
}
+ $pageLang = $this->getFunctionLang();
+
switch ( $index ) {
case 'currentmonth':
- $value = $wgContLang->formatNum( gmdate( 'm', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'm', $ts ) );
break;
case 'currentmonth1':
- $value = $wgContLang->formatNum( gmdate( 'n', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'n', $ts ) );
break;
case 'currentmonthname':
- $value = $wgContLang->getMonthName( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthName( gmdate( 'n', $ts ) );
break;
case 'currentmonthnamegen':
- $value = $wgContLang->getMonthNameGen( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthNameGen( gmdate( 'n', $ts ) );
break;
case 'currentmonthabbrev':
- $value = $wgContLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
break;
case 'currentday':
- $value = $wgContLang->formatNum( gmdate( 'j', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'j', $ts ) );
break;
case 'currentday2':
- $value = $wgContLang->formatNum( gmdate( 'd', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'd', $ts ) );
break;
case 'localmonth':
- $value = $wgContLang->formatNum( $localMonth );
+ $value = $pageLang->formatNum( $localMonth );
break;
case 'localmonth1':
- $value = $wgContLang->formatNum( $localMonth1 );
+ $value = $pageLang->formatNum( $localMonth1 );
break;
case 'localmonthname':
- $value = $wgContLang->getMonthName( $localMonthName );
+ $value = $pageLang->getMonthName( $localMonthName );
break;
case 'localmonthnamegen':
- $value = $wgContLang->getMonthNameGen( $localMonthName );
+ $value = $pageLang->getMonthNameGen( $localMonthName );
break;
case 'localmonthabbrev':
- $value = $wgContLang->getMonthAbbreviation( $localMonthName );
+ $value = $pageLang->getMonthAbbreviation( $localMonthName );
break;
case 'localday':
- $value = $wgContLang->formatNum( $localDay );
+ $value = $pageLang->formatNum( $localDay );
break;
case 'localday2':
- $value = $wgContLang->formatNum( $localDay2 );
+ $value = $pageLang->formatNum( $localDay2 );
break;
case 'pagename':
$value = wfEscapeWikiText( $this->mTitle->getText() );
$value = ( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
break;
case 'currentdayname':
- $value = $wgContLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
+ $value = $pageLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
break;
case 'currentyear':
- $value = $wgContLang->formatNum( gmdate( 'Y', $ts ), true );
+ $value = $pageLang->formatNum( gmdate( 'Y', $ts ), true );
break;
case 'currenttime':
- $value = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
+ $value = $pageLang->time( wfTimestamp( TS_MW, $ts ), false, false );
break;
case 'currenthour':
- $value = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
+ $value = $pageLang->formatNum( gmdate( 'H', $ts ), true );
break;
case 'currentweek':
# @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
- $value = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
+ $value = $pageLang->formatNum( (int)gmdate( 'W', $ts ) );
break;
case 'currentdow':
- $value = $wgContLang->formatNum( gmdate( 'w', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'w', $ts ) );
break;
case 'localdayname':
- $value = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
+ $value = $pageLang->getWeekdayName( $localDayOfWeek + 1 );
break;
case 'localyear':
- $value = $wgContLang->formatNum( $localYear, true );
+ $value = $pageLang->formatNum( $localYear, true );
break;
case 'localtime':
- $value = $wgContLang->time( $localTimestamp, false, false );
+ $value = $pageLang->time( $localTimestamp, false, false );
break;
case 'localhour':
- $value = $wgContLang->formatNum( $localHour, true );
+ $value = $pageLang->formatNum( $localHour, true );
break;
case 'localweek':
# @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
- $value = $wgContLang->formatNum( (int)$localWeek );
+ $value = $pageLang->formatNum( (int)$localWeek );
break;
case 'localdow':
- $value = $wgContLang->formatNum( $localDayOfWeek );
+ $value = $pageLang->formatNum( $localDayOfWeek );
break;
case 'numberofarticles':
- $value = $wgContLang->formatNum( SiteStats::articles() );
+ $value = $pageLang->formatNum( SiteStats::articles() );
break;
case 'numberoffiles':
- $value = $wgContLang->formatNum( SiteStats::images() );
+ $value = $pageLang->formatNum( SiteStats::images() );
break;
case 'numberofusers':
- $value = $wgContLang->formatNum( SiteStats::users() );
+ $value = $pageLang->formatNum( SiteStats::users() );
break;
case 'numberofactiveusers':
- $value = $wgContLang->formatNum( SiteStats::activeUsers() );
+ $value = $pageLang->formatNum( SiteStats::activeUsers() );
break;
case 'numberofpages':
- $value = $wgContLang->formatNum( SiteStats::pages() );
+ $value = $pageLang->formatNum( SiteStats::pages() );
break;
case 'numberofadmins':
- $value = $wgContLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
+ $value = $pageLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
break;
case 'numberofedits':
- $value = $wgContLang->formatNum( SiteStats::edits() );
+ $value = $pageLang->formatNum( SiteStats::edits() );
break;
case 'numberofviews':
- $value = $wgContLang->formatNum( SiteStats::views() );
+ $value = $pageLang->formatNum( SiteStats::views() );
break;
case 'currenttimestamp':
$value = wfTimestamp( TS_MW, $ts );
case 'stylepath':
return $wgStylePath;
case 'directionmark':
- return $wgContLang->getDirMark();
+ return $pageLang->getDirMark();
case 'contentlanguage':
global $wgLanguageCode;
return $wgLanguageCode;
}
}
- if ( $index )
+ if ( $index ) {
$this->mVarCache[$index] = $value;
+ }
return $value;
}
* dependency requirements.
*
* @private
+ *
+ * @return PPNode
*/
function preprocessToDom( $text, $flags = 0 ) {
$dom = $this->getPreprocessor()->preprocessToObj( $text, $flags );
/**
* Return a three-element array: leading whitespace, string contents, trailing whitespace
+ *
+ * @param $s string
+ *
+ * @return array
*/
public static function splitWhitespace( $s ) {
$ltrimmed = ltrim( $s );
* self::OT_PREPROCESS: templates but not extension tags
* self::OT_HTML: all templates and extension tags
*
- * @param $text String: the text to transform
+ * @param $text String the text to transform
* @param $frame PPFrame Object describing the arguments passed to the template.
* Arguments may also be provided as an associative array, as was the usual case before MW1.12.
* Providing arguments this way may be useful for extensions wishing to perform variable replacement explicitly.
- * @param $argsOnly Boolean: only do argument (triple-brace) expansion, not double-brace expansion
+ * @param $argsOnly Boolean only do argument (triple-brace) expansion, not double-brace expansion
* @private
+ *
+ * @return string
*/
function replaceVariables( $text, $frame = false, $argsOnly = false ) {
# Is there any text? Also, Prevent too big inclusions!
return $text;
}
- # Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
+ /**
+ * Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
+ *
+ * @param $args array
+ *
+ * @return array
+ */
static function createAssocArgs( $args ) {
$assocArgs = array();
$index = 1;
$originalTitle = $part1;
# $args is a list of argument nodes, starting from index 0, not including $part1
- # *** FIXME if piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object
+ # @todo FIXME: If piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object
$args = ( null == $piece['parts'] ) ? array() : $piece['parts'];
wfProfileOut( __METHOD__.'-setup' );
+ wfProfileIn( __METHOD__."-title-$originalTitle" );
# SUBST
wfProfileIn( __METHOD__.'-modifiers' );
&& $this->mOptions->getAllowSpecialInclusion()
&& $this->ot['html'] )
{
- $text = SpecialPage::capturePath( $title );
- if ( is_string( $text ) ) {
+ $pageArgs = array();
+ for ( $i = 0; $i < $args->getLength(); $i++ ) {
+ $bits = $args->item( $i )->splitArg();
+ if ( strval( $bits['index'] ) === '' ) {
+ $name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
+ $value = trim( $frame->expand( $bits['value'] ) );
+ $pageArgs[$name] = $value;
+ }
+ }
+ $context = new RequestContext;
+ $context->setTitle( $title );
+ $context->setRequest( new FauxRequest( $pageArgs ) );
+ $context->setUser( $this->getUser() );
+ $context->setLang( Language::factory( $this->mOptions->getUserLang() ) );
+ $ret = SpecialPageFactory::capturePath( $title, $context );
+ if ( $ret ) {
+ $text = $context->getOutput()->getHTML();
+ $this->mOutput->addOutputPageMetadata( $context->getOutput() );
$found = true;
$isHTML = true;
$this->disableCache();
# Recover the source wikitext and return it
if ( !$found ) {
$text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args );
+ wfProfileOut( __METHOD__."-title-$originalTitle" );
wfProfileOut( __METHOD__ );
return array( 'object' => $text );
}
$ret = array( 'text' => $text );
}
+ wfProfileOut( __METHOD__."-title-$originalTitle" );
wfProfileOut( __METHOD__ );
return $ret;
}
/**
* Get the semi-parsed DOM representation of a template with a given title,
* and its redirect destination title. Cached.
+ *
+ * @param $title Title
+ *
+ * @return array
*/
function getTemplateDom( $title ) {
$cacheTitle = $title;
/**
* Static function to get a template
* Can be overridden via ParserOptions::setTemplateCallback().
+ *
+ * @parma $title Title
+ * @param $parser Parser
+ *
+ * @return array
*/
- static function statelessFetchTemplate( $title, $parser=false ) {
+ static function statelessFetchTemplate( $title, $parser = false ) {
$text = $skip = false;
$finalTitle = $title;
$deps = array();
* @param Title $title
* @param string $time MW timestamp
* @param string $sha1 base 36 SHA-1
- * @return mixed File or false
+ * @return File|false
*/
function fetchFile( $title, $time = false, $sha1 = false ) {
$res = $this->fetchFileAndTitle( $title, $time, $sha1 );
# Register the file as a dependency...
$this->mOutput->addImage( $title->getDBkey(), $time, $sha1 );
if ( $file && !$title->equals( $file->getTitle() ) ) {
- # We fetched a rev from a different title; register it too...
- $this->mOutput->addImage( $file->getTitle()->getDBkey(), $time, $sha1 );
- # Update fetched file title
+ # Update fetched file title
$title = $file->getTitle();
+ if ( is_null( $file->getRedirectedTitle() ) ) {
+ # This file was not a redirect, but the title does not match.
+ # Register under the new name because otherwise the link will
+ # get lost.
+ $this->mOutput->addImage( $title->getDBkey(), $time, $sha1 );
+ }
}
return array( $file, $title );
}
/**
* Transclude an interwiki link.
+ *
+ * @param $title Title
+ * @param $action
+ *
+ * @return string
*/
function interwikiTransclude( $title, $action ) {
global $wgEnableScaryTranscluding;
return $this->fetchScaryTemplateMaybeFromCache( $url );
}
+ /**
+ * @param $url string
+ * @return Mixed|String
+ */
function fetchScaryTemplateMaybeFromCache( $url ) {
global $wgTranscludeCacheExpiry;
$dbr = wfGetDB( DB_SLAVE );
return $text;
}
-
/**
* Triple brace replacement -- used for template arguments
* @private
+ *
+ * @param $peice array
+ * @param $frame PPFrame
+ *
+ * @return array
*/
function argSubstitution( $piece, $frame ) {
wfProfileIn( __METHOD__ );
* inner Contents of extension element
* noClose Original text did not have a close tag
* @param $frame PPFrame
+ *
+ * @return string
*/
function extensionSubstitution( $params, $frame ) {
$name = $frame->expand( $params['name'] );
/**
* Strip double-underscore items like __NOGALLERY__ and __NOTOC__
* Fills $this->mDoubleUnderscores, returns the modified text
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doDoubleUnderscore( $text ) {
wfProfileIn( __METHOD__ );
}
# (bug 8068) Allow control over whether robots index a page.
#
- # FIXME (bug 14899): __INDEX__ always overrides __NOINDEX__ here! This
+ # @todo FIXME: Bug 14899: __INDEX__ always overrides __NOINDEX__ here! This
# is not desirable, the last one on the page should win.
if ( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
$this->mOutput->setIndexPolicy( 'noindex' );
* @private
*/
function formatHeadings( $text, $origText, $isMain=true ) {
- global $wgMaxTocLevel, $wgContLang, $wgHtml5, $wgExperimentalHtmlIds;
+ global $wgMaxTocLevel, $wgHtml5, $wgExperimentalHtmlIds;
# Inhibit editsection links if requested in the page
if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
if ( $dot ) {
$numbering .= '.';
}
- $numbering .= $wgContLang->formatNum( $sublevelCount[$i] );
+ $numbering .= $this->getFunctionLang()->formatNum( $sublevelCount[$i] );
$dot = 1;
}
}
# HTML names must be case-insensitively unique (bug 10721).
# This does not apply to Unicode characters per
# http://dev.w3.org/html5/spec/infrastructure.html#case-sensitivity-and-string-comparison
- # FIXME: We may be changing them depending on the current locale.
+ # @todo FIXME: We may be changing them depending on the current locale.
$arrayKey = strtolower( $safeHeadline );
if ( $legacyHeadline === false ) {
$legacyArrayKey = false;
}
# split up and insert constructed headlines
-
$blocks = preg_split( '/<H[1-6].*?' . '>.*?<\/H[1-6]>/i', $text );
$i = 0;
+ // build an array of document sections
+ $sections = array();
foreach ( $blocks as $block ) {
- if ( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
- # This is the [edit] link that appears for the top block of text when
- # section editing is enabled
-
- # Disabled because it broke block formatting
- # For example, a bullet point in the top line
- # $full .= $sk->editSectionLink(0);
- }
- $full .= $block;
- if ( $enoughToc && !$i && $isMain && !$this->mForceTocPosition ) {
- # Top anchor now in skin
- $full = $full.$toc;
+ // $head is zero-based, sections aren't.
+ if ( empty( $head[$i - 1] ) ) {
+ $sections[$i] = $block;
+ } else {
+ $sections[$i] = $head[$i - 1] . $block;
}
- if ( !empty( $head[$i] ) ) {
- $full .= $head[$i];
- }
+ /**
+ * Send a hook, one per section.
+ * The idea here is to be able to make section-level DIVs, but to do so in a
+ * lower-impact, more correct way than r50769
+ *
+ * $this : caller
+ * $section : the section number
+ * &$sectionContent : ref to the content of the section
+ * $showEditLinks : boolean describing whether this section has an edit link
+ */
+ wfRunHooks( 'ParserSectionCreate', array( $this, $i, &$sections[$i], $showEditLink ) );
+
$i++;
}
+
+ if ( $enoughToc && $isMain && !$this->mForceTocPosition ) {
+ // append the TOC at the beginning
+ // Top anchor now in skin
+ $sections[0] = $sections[0] . $toc . "\n";
+ }
+
+ $full .= join( '', $sections );
+
if ( $this->mForceTocPosition ) {
return str_replace( '<!--MWTOC-->', $toc, $full );
} else {
/**
* Pre-save transform helper function
* @private
+ *
+ * @param $text string
+ * @param $user User
+ *
+ * @return string
*/
function pstPass2( $text, $user ) {
global $wgContLang, $wgLocaltimezone;
$tc = "[$wgLegalTitleChars]";
$nc = '[ _0-9A-Za-z\x80-\xff-]'; # Namespaces can use non-ascii!
- $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\))\\|]]/"; # [[ns:page (context)|]]
- $p4 = "/\[\[(:?$nc+:|:|)($tc+?)(($tc+))\\|]]/"; # [[ns:page(context)|]]
- $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\)|)(, $tc+|)\\|]]/"; # [[ns:page (context), context|]]
+ $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\))\\|]]/"; # [[ns:page (context)|]]
+ $p4 = "/\[\[(:?$nc+:|:|)($tc+?)( ?($tc+))\\|]]/"; # [[ns:page(context)|]]
+ $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\)|)(, $tc+|)\\|]]/"; # [[ns:page (context), context|]]
$p2 = "/\[\[\\|($tc+)]]/"; # [[|page]]
# try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
* as it may have changed if it's the $wgParser.
*
* @param $user User
- * @param $nickname String: nickname to use or false to use user's default nickname
- * @param $fancySig Boolean: whether the nicknname is the complete signature
+ * @param $nickname String|bool nickname to use or false to use user's default nickname
+ * @param $fancySig Boolean|null whether the nicknname is the complete signature
* or null to use default value
* @return string
*/
# If we're still here, make it a link to the user page
$userText = wfEscapeWikiText( $username );
$nickText = wfEscapeWikiText( $nickname );
- if ( $user->isAnon() ) {
- return wfMsgExt( 'signature-anon', array( 'content', 'parsemag' ), $userText, $nickText );
- } else {
- return wfMsgExt( 'signature', array( 'content', 'parsemag' ), $userText, $nickText );
- }
+ $msgName = $user->isAnon() ? 'signature-anon' : 'signature';
+
+ return wfMessage( $msgName, $userText, $nickText )->inContentLanguage()->title( $this->getTitle() )->text();
}
/**
* 2) Substitute all transclusions
*
* @param $text String
- * @param $parsing Whether we're cleaning (preferences save) or parsing
+ * @param $parsing bool Whether we're cleaning (preferences save) or parsing
* @return String: signature text
*/
function cleanSig( $text, $parsing = false ) {
if ( !$parsing ) {
global $wgTitle;
- $this->mOptions = new ParserOptions;
- $this->clearState();
- $this->setTitle( $wgTitle );
- $this->setOutputType = self::OT_PREPROCESS;
+ $this->startParse( $wgTitle, new ParserOptions, self::OT_PREPROCESS, true );
}
# Option to disable this feature
return $text;
}
- # FIXME: regex doesn't respect extension tags or nowiki
+ # @todo FIXME: Regex doesn't respect extension tags or nowiki
# => Move this logic to braceSubstitution()
$substWord = MagicWord::get( 'subst' );
$substRegex = '/\{\{(?!(?:' . $substWord->getBaseRegex() . '))/x' . $substWord->getRegexCase();
/**
* Set up some variables which are usually set up in parse()
* so that an external function can call some class members with confidence
+ *
+ * @param $title Title|null
+ * @param $options ParserOptions
+ * @param $outputType
+ * @param $clearState bool
*/
public function startExternalParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
$this->startParse( $title, $options, $outputType, $clearState );
}
+ /**
+ * @param $title Title|null
+ * @param $options ParserOptions
+ * @param $outputType
+ * @param $clearState bool
+ */
private function startParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
$this->setTitle( $title );
$this->mOptions = $options;
/**
* Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
* The callback should have the following form:
- * function myParserHook( $text, $params, $parser ) { ... }
+ * function myParserHook( $text, $params, $parser, $frame ) { ... }
*
* Transform and return $text. Use $parser for any required context, e.g. use
* $parser->getTitle() and $parser->getOptions() not $wgTitle or $wgOut->mParserOptions
*
+ * Hooks may return extended information by returning an array, of which the
+ * first numbered element (index 0) must be the return string, and all other
+ * entries are extracted into local variables within an internal function
+ * in the Parser class.
+ *
+ * This interface (introduced r61913) appears to be undocumented, but
+ * 'markerName' is used by some core tag hooks to override which strip
+ * array their results are placed in. **Use great caution if attempting
+ * this interface, as it is not documented and injudicious use could smash
+ * private variables.**
+ *
* @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
* @param $callback Mixed: the callback function (and object) to use for the tag
* @return The old value of the mTagHooks array associated with the hook
return $oldVal;
}
+ /**
+ * As setHook(), but letting the contents be parsed.
+ *
+ * Transparent tag hooks are like regular XML-style tag hooks, except they
+ * operate late in the transformation sequence, on HTML instead of wikitext.
+ *
+ * This is probably obsoleted by things dealing with parser frames?
+ * The only extension currently using it is geoserver.
+ *
+ * @since 1.10
+ * @todo better document or deprecate this
+ *
+ * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+ * @param $callback Mixed: the callback function (and object) to use for the tag
+ * @return The old value of the mTagHooks array associated with the hook
+ */
function setTransparentTagHook( $tag, $callback ) {
$tag = strtolower( $tag );
if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setTransparentHook('$tag', ...) call" );
$this->mStripList = $this->mDefaultStripList;
}
+ /**
+ * Remove a specific tag hook. Should not be called on $wgParser.
+ * Does not change the strip list.
+ *
+ * @param string $tag
+ * @return void
+ */
+ function clearTagHook( $tag ) {
+ if ( isset( $this->mTagHooks[$tag] ) ) {
+ unset( $this->mTagHooks[$tag] );
+ }
+ }
+
/**
* Create a function, e.g. {{sum:1|2|3}}
* The callback function should have the form:
}
/**
- * FIXME: update documentation. makeLinkObj() is deprecated.
+ * @todo FIXME: Update documentation. makeLinkObj() is deprecated.
* Replace <!--LINK--> link placeholders with actual links, in the buffer
* Placeholders created in Skin::makeLinkObj()
- * Returns an array of link CSS classes, indexed by PDBK.
+ *
+ * @param $text string
+ * @param $options int
+ *
+ * @return array of link CSS classes, indexed by PDBK.
*/
function replaceLinkHolders( &$text, $options = 0 ) {
return $this->mLinkHolders->replace( $text );
* given as text will return the HTML of a gallery with two images,
* labeled 'The number "1"' and
* 'A tree'.
+ *
+ * @param string $text
+ * @param array $params
+ * @return string HTML
*/
function renderImageGallery( $text, $params ) {
$ig = new ImageGallery();
if ( strpos( $matches[0], '%' ) !== false ) {
$matches[1] = rawurldecode( $matches[1] );
}
- $tp = Title::newFromText( $matches[1], NS_FILE );
- $nt =& $tp;
- if ( is_null( $nt ) ) {
+ $title = Title::newFromText( $matches[1], NS_FILE );
+ if ( is_null( $title ) ) {
# Bogus title. Ignore these so we don't bomb out later.
continue;
}
+
+ $label = '';
+ $alt = '';
if ( isset( $matches[3] ) ) {
- $label = $matches[3];
- } else {
- $label = '';
+ // look for an |alt= definition while trying not to break existing
+ // captions with multiple pipes (|) in it, until a more sensible grammar
+ // is defined for images in galleries
+
+ $matches[3] = $this->recursiveTagParse( trim( $matches[3] ) );
+ $altmatches = StringUtils::explode('|', $matches[3]);
+ $magicWordAlt = MagicWord::get( 'img_alt' );
+
+ foreach ( $altmatches as $altmatch ) {
+ $match = $magicWordAlt->matchVariableStartToEnd( $altmatch );
+ if ( $match ) {
+ $alt = $this->stripAltText( $match, false );
+ }
+ else {
+ // concatenate all other pipes
+ $label .= '|' . $altmatch;
+ }
+ }
+ // remove the first pipe
+ $label = substr( $label, 1 );
}
- $html = $this->recursiveTagParse( trim( $label ) );
-
- $ig->add( $nt, $html );
+ $ig->add( $title, $label, $alt );
}
return $ig->toHTML();
}
+ /**
+ * @param $handler
+ * @return array
+ */
function getImageParams( $handler ) {
if ( $handler ) {
$handlerClass = get_class( $handler );
*
* @param $title Title
* @param $options String
- * @param $holders LinkHolderArray
+ * @param $holders LinkHolderArray|false
* @return string HTML
*/
function makeImage( $title, $options, $holders = false ) {
list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+ if ( !$file ) {
+ $this->addTrackingCategory( 'broken-file-category' );
+ }
+
# Process the input parameters
$caption = '';
$params = array( 'frame' => array(), 'handler' => array(),
switch( $paramName ) {
case 'manualthumb':
case 'alt':
- # @todo Fixme: possibly check validity here for
+ # @todo FIXME: Possibly check validity here for
# manualthumb? downstream behavior seems odd with
# missing manual thumbs.
$validated = true;
$value = true;
$validated = true;
} elseif ( preg_match( "/^$prots/", $value ) ) {
- if ( preg_match( "/^($prots)$chars+$/", $value, $m ) ) {
+ if ( preg_match( "/^($prots)$chars+$/u", $value, $m ) ) {
$paramName = 'link-url';
$this->mOutput->addExternalLink( $value );
if ( $this->mOptions->getExternalLinkTarget() ) {
return $ret;
}
+ /**
+ * @param $caption
+ * @param $holders LinkHolderArray
+ * @return mixed|String
+ */
protected function stripAltText( $caption, $holders ) {
# Strip bad stuff out of the title (tooltip). We can't just use
# replaceLinkHoldersText() here, because if this function is called
* @param $text String
* @param $frame PPFrame
* @return String
- * @private
*/
function attributeStripCallback( &$text, $frame = false ) {
$text = $this->replaceVariables( $text, $frame );
/**
* Accessor
+ *
+ * @return array
*/
function getTags() {
return array_merge( array_keys( $this->mTransparentTagHooks ), array_keys( $this->mTagHooks ) );
*
* Transparent tag hooks are like regular XML-style tag hooks, except they
* operate late in the transformation sequence, on HTML instead of wikitext.
+ *
+ * @param $text string
+ *
+ * @return string
*/
function replaceTransparentTags( $text ) {
$matches = array();
$elements = array_keys( $this->mTransparentTagHooks );
- $text = $this->extractTagsAndParams( $elements, $text, $matches, $this->mUniqPrefix );
+ $text = self::extractTagsAndParams( $elements, $text, $matches, $this->mUniqPrefix );
+ $replacements = array();
foreach ( $matches as $marker => $data ) {
list( $element, $content, $params, $tag ) = $data;
} else {
$output = $tag;
}
- $this->mStripState->addGeneral( $marker, $output );
+ $replacements[$marker] = $output;
}
- return $text;
+ return strtr( $text, $replacements );
}
/**
* @param $newText String: replacement text for section data.
* @return String: for "get", the extracted section text.
* for "replace", the whole page with the section replaced.
+ * If the page is empty and section 0 is requested, $text (as '')
+ * is returned
*/
private function extractSections( $text, $section, $mode, $newText='' ) {
global $wgTitle; # not generally used but removes an ugly failure mode
* This function returns $oldtext after the content of the section
* specified by $section has been replaced with $text.
*
- * @param $text String: former text of the article
+ * @param $oldtext String: former text of the article
* @param $section Numeric: section identifier
* @param $text String: replacing text
- * #return String: modified text
+ * @return String: modified text
*/
public function replaceSection( $oldtext, $section, $text ) {
return $this->extractSections( $oldtext, $section, "replace", $text );
/**
* Get the revision object for $this->mRevisionId
*
- * @return either a Revision object or null
+ * @return Revision|null either a Revision object or null
*/
protected function getRevisionObject() {
if ( !is_null( $this->mRevisionObject ) ) {
* Try to guess the section anchor name based on a wikitext fragment
* presumably extracted from a heading, for example "Header" from
* "== Header ==".
+ *
+ * @param $text string
+ *
+ * @return string
*/
public function guessSectionNameFromWikiText( $text ) {
# Strip out wikitext links(they break the anchor)
$text = preg_replace( '/\[\[:?([^[|]+)\|([^[]+)\]\]/', '$2', $text );
$text = preg_replace( '/\[\[:?([^[]+)\|?\]\]/', '$1', $text );
- # Strip external link markup (FIXME: Not Tolerant to blank link text
+ # Strip external link markup
+ # @todo FIXME: Not tolerant to blank link text
# I.E. [http://www.mediawiki.org] will render as [1] or something depending
# on how many empty links there are on the page - need to figure that out.
$text = preg_replace( '/\[(?:' . wfUrlProtocols() . ')([^ ]+?) ([^[]+)\]/', '$2', $text );
/**
* strip/replaceVariables/unstrip for preprocessor regression testing
+ *
+ * @param $text string
+ * @param $title Title
+ * @param $options ParserOptions
+ * @param $outputType int
+ *
+ * @return string
*/
- function testSrvus( $text, $title, ParserOptions $options, $outputType = self::OT_HTML ) {
- if ( !$title instanceof Title ) {
- $title = Title::newFromText( $title );
- }
+ function testSrvus( $text, Title $title, ParserOptions $options, $outputType = self::OT_HTML ) {
$this->startParse( $title, $options, $outputType, true );
$text = $this->replaceVariables( $text );
return $text;
}
- function testPst( $text, $title, $options ) {
- global $wgUser;
- if ( !$title instanceof Title ) {
- $title = Title::newFromText( $title );
- }
- return $this->preSaveTransform( $text, $title, $wgUser, $options );
+ /**
+ * @param $text string
+ * @param $title Title
+ * @param $options ParserOptions
+ * @return string
+ */
+ function testPst( $text, Title $title, ParserOptions $options ) {
+ return $this->preSaveTransform( $text, $title, $options->getUser(), $options );
}
- function testPreprocess( $text, $title, $options ) {
- if ( !$title instanceof Title ) {
- $title = Title::newFromText( $title );
- }
+ /**
+ * @param $text
+ * @param $title Title
+ * @param $options ParserOptions
+ * @return string
+ */
+ function testPreprocess( $text, Title $title, ParserOptions $options ) {
return $this->testSrvus( $text, $title, $options, self::OT_PREPROCESS );
}
* This will call the callback function twice, with 'aaa' and 'bbb'. Those
* two strings will be replaced with the value returned by the callback in
* each case.
+ *
+ * @param $s string
+ * @param $callback
+ *
+ * @return string
*/
function markerSkipCallback( $s, $callback ) {
$i = 0;
* array can later be loaded into another parser instance with
* unserializeHalfParsedText(). The text can then be safely incorporated into
* the return value of a parser hook.
+ *
+ * @param $text string
+ *
+ * @return array
*/
function serializeHalfParsedText( $text ) {
wfProfileIn( __METHOD__ );
* serializeHalfParsedText(), is compatible with the current version of the
* parser.
*
- * @param $data Array.
+ * @param $data Array
+ *
+ * @return bool
*/
function isValidHalfParsedText( $data ) {
return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;