summaryrefslogtreecommitdiff
path: root/includes/parser
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
committerPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
commit63601400e476c6cf43d985f3e7b9864681695ed4 (patch)
treef7846203a952e38aaf66989d0a4702779f549962 /includes/parser
parent8ff01378c9e0207f9169b81966a51def645b6a51 (diff)
Update to MediaWiki 1.20.2
this update includes: * adjusted Arch Linux skin * updated FluxBBAuthPlugin * patch for https://bugzilla.wikimedia.org/show_bug.cgi?id=44024
Diffstat (limited to 'includes/parser')
-rw-r--r--includes/parser/CacheTime.php132
-rw-r--r--includes/parser/CoreLinkFunctions.php16
-rw-r--r--includes/parser/CoreParserFunctions.php169
-rw-r--r--includes/parser/CoreTagHooks.php16
-rw-r--r--includes/parser/DateFormatter.php91
-rw-r--r--includes/parser/LinkHolderArray.php32
-rw-r--r--includes/parser/Parser.php373
-rw-r--r--includes/parser/ParserCache.php33
-rw-r--r--includes/parser/ParserOptions.php55
-rw-r--r--includes/parser/ParserOutput.php175
-rw-r--r--includes/parser/Parser_DiffTest.php16
-rw-r--r--includes/parser/Parser_LinkHooks.php20
-rw-r--r--includes/parser/Preprocessor.php28
-rw-r--r--includes/parser/Preprocessor_DOM.php80
-rw-r--r--includes/parser/Preprocessor_Hash.php78
-rw-r--r--includes/parser/Preprocessor_HipHop.hphp106
-rw-r--r--includes/parser/StripState.php55
-rw-r--r--includes/parser/Tidy.php22
18 files changed, 1071 insertions, 426 deletions
diff --git a/includes/parser/CacheTime.php b/includes/parser/CacheTime.php
new file mode 100644
index 00000000..881dded7
--- /dev/null
+++ b/includes/parser/CacheTime.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Parser cache specific expiry check.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Parser
+ */
+
+/**
+ * Parser cache specific expiry check.
+ *
+ * @ingroup Parser
+ */
+class CacheTime {
+
+ var $mVersion = Parser::VERSION, # Compatibility check
+ $mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
+ $mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncachable. Used in ParserCache.
+ $mContainsOldMagic; # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
+
+ function getCacheTime() { return $this->mCacheTime; }
+
+ function containsOldMagic() { return $this->mContainsOldMagic; }
+ function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
+
+ /**
+ * setCacheTime() sets the timestamp expressing when the page has been rendered.
+ * This doesn not control expiry, see updateCacheExpiry() for that!
+ * @param $t string
+ * @return string
+ */
+ function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
+
+ /**
+ * Sets the number of seconds after which this object should expire.
+ * This value is used with the ParserCache.
+ * If called with a value greater than the value provided at any previous call,
+ * the new call has no effect. The value returned by getCacheExpiry is smaller
+ * or equal to the smallest number that was provided as an argument to
+ * updateCacheExpiry().
+ *
+ * @param $seconds number
+ */
+ function updateCacheExpiry( $seconds ) {
+ $seconds = (int)$seconds;
+
+ if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
+ $this->mCacheExpiry = $seconds;
+ }
+
+ // hack: set old-style marker for uncacheable entries.
+ if ( $this->mCacheExpiry !== null && $this->mCacheExpiry <= 0 ) {
+ $this->mCacheTime = -1;
+ }
+ }
+
+ /**
+ * Returns the number of seconds after which this object should expire.
+ * This method is used by ParserCache to determine how long the ParserOutput can be cached.
+ * The timestamp of expiry can be calculated by adding getCacheExpiry() to getCacheTime().
+ * The value returned by getCacheExpiry is smaller or equal to the smallest number
+ * that was provided to a call of updateCacheExpiry(), and smaller or equal to the
+ * value of $wgParserCacheExpireTime.
+ * @return int|mixed|null
+ */
+ function getCacheExpiry() {
+ global $wgParserCacheExpireTime;
+
+ if ( $this->mCacheTime < 0 ) {
+ return 0;
+ } // old-style marker for "not cachable"
+
+ $expire = $this->mCacheExpiry;
+
+ if ( $expire === null ) {
+ $expire = $wgParserCacheExpireTime;
+ } else {
+ $expire = min( $expire, $wgParserCacheExpireTime );
+ }
+
+ if( $this->containsOldMagic() ) { //compatibility hack
+ $expire = min( $expire, 3600 ); # 1 hour
+ }
+
+ if ( $expire <= 0 ) {
+ return 0; // not cachable
+ } else {
+ return $expire;
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ function isCacheable() {
+ return $this->getCacheExpiry() > 0;
+ }
+
+ /**
+ * Return true if this cached output object predates the global or
+ * per-article cache invalidation timestamps, or if it comes from
+ * an incompatible older version.
+ *
+ * @param $touched String: the affected article's last touched timestamp
+ * @return Boolean
+ */
+ public function expired( $touched ) {
+ global $wgCacheEpoch;
+ return !$this->isCacheable() || // parser says it's uncacheable
+ $this->getCacheTime() < $touched ||
+ $this->getCacheTime() <= $wgCacheEpoch ||
+ $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
+ !isset( $this->mVersion ) ||
+ version_compare( $this->mVersion, Parser::VERSION, "lt" );
+ }
+
+}
diff --git a/includes/parser/CoreLinkFunctions.php b/includes/parser/CoreLinkFunctions.php
index 8de13278..4bfa9d35 100644
--- a/includes/parser/CoreLinkFunctions.php
+++ b/includes/parser/CoreLinkFunctions.php
@@ -2,7 +2,23 @@
/**
* Link functions provided by MediaWiki core; experimental
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
diff --git a/includes/parser/CoreParserFunctions.php b/includes/parser/CoreParserFunctions.php
index 0e5702b7..8917b6d0 100644
--- a/includes/parser/CoreParserFunctions.php
+++ b/includes/parser/CoreParserFunctions.php
@@ -2,7 +2,23 @@
/**
* Parser functions provided by MediaWiki core
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -55,6 +71,7 @@ class CoreParserFunctions {
$parser->setFunctionHook( 'padright', array( __CLASS__, 'padright' ), SFH_NO_HASH );
$parser->setFunctionHook( 'anchorencode', array( __CLASS__, 'anchorencode' ), SFH_NO_HASH );
$parser->setFunctionHook( 'special', array( __CLASS__, 'special' ) );
+ $parser->setFunctionHook( 'speciale', array( __CLASS__, 'speciale' ) );
$parser->setFunctionHook( 'defaultsort', array( __CLASS__, 'defaultsort' ), SFH_NO_HASH );
$parser->setFunctionHook( 'filepath', array( __CLASS__, 'filepath' ), SFH_NO_HASH );
$parser->setFunctionHook( 'pagesincategory', array( __CLASS__, 'pagesincategory' ), SFH_NO_HASH );
@@ -62,6 +79,7 @@ class CoreParserFunctions {
$parser->setFunctionHook( 'protectionlevel', array( __CLASS__, 'protectionlevel' ), SFH_NO_HASH );
$parser->setFunctionHook( 'namespace', array( __CLASS__, 'mwnamespace' ), SFH_NO_HASH );
$parser->setFunctionHook( 'namespacee', array( __CLASS__, 'namespacee' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'namespacenumber', array( __CLASS__, 'namespacenumber' ), SFH_NO_HASH );
$parser->setFunctionHook( 'talkspace', array( __CLASS__, 'talkspace' ), SFH_NO_HASH );
$parser->setFunctionHook( 'talkspacee', array( __CLASS__, 'talkspacee' ), SFH_NO_HASH );
$parser->setFunctionHook( 'subjectspace', array( __CLASS__, 'subjectspace' ), SFH_NO_HASH );
@@ -111,7 +129,8 @@ class CoreParserFunctions {
* @return mixed|string
*/
static function formatDate( $parser, $date, $defaultPref = null ) {
- $df = DateFormatter::getInstance();
+ $lang = $parser->getFunctionLang();
+ $df = DateFormatter::getInstance( $lang );
$date = trim( $date );
@@ -158,6 +177,7 @@ class CoreParserFunctions {
* @param $parser Parser object
* @param $s String: The text to encode.
* @param $arg String (optional): The type of encoding.
+ * @return string
*/
static function urlencode( $parser, $s = '', $arg = null ) {
static $magicWords = null;
@@ -283,8 +303,10 @@ class CoreParserFunctions {
// Some shortcuts to avoid loading user data unnecessarily
if ( count( $forms ) === 0 ) {
+ wfProfileOut( __METHOD__ );
return '';
} elseif ( count( $forms ) === 1 ) {
+ wfProfileOut( __METHOD__ );
return $forms[0];
}
@@ -303,9 +325,9 @@ class CoreParserFunctions {
// check parameter, or use the ParserOptions if in interface message
$user = User::newFromName( $username );
if ( $user ) {
- $gender = $user->getOption( 'gender' );
+ $gender = GenderCache::singleton()->getGenderOf( $user, __METHOD__ );
} elseif ( $username === '' && $parser->getOptions()->getInterfaceMessage() ) {
- $gender = $parser->getOptions()->getUser()->getOption( 'gender' );
+ $gender = GenderCache::singleton()->getGenderOf( $parser->getOptions()->getUser(), __METHOD__ );
}
$ret = $parser->getFunctionLang()->gender( $gender, $forms );
wfProfileOut( __METHOD__ );
@@ -320,6 +342,7 @@ class CoreParserFunctions {
static function plural( $parser, $text = '' ) {
$forms = array_slice( func_get_args(), 2 );
$text = $parser->getFunctionLang()->parseFormattedNumber( $text );
+ settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
return $parser->getFunctionLang()->convertPlural( $text, $forms );
}
@@ -420,6 +443,7 @@ class CoreParserFunctions {
* corresponding magic word
* Note: function name changed to "mwnamespace" rather than "namespace"
* to not break PHP 5.3
+ * @return mixed|string
*/
static function mwnamespace( $parser, $title = null ) {
$t = Title::newFromText( $title );
@@ -433,6 +457,12 @@ class CoreParserFunctions {
return '';
return wfUrlencode( $t->getNsText() );
}
+ static function namespacenumber( $parser, $title = null ) {
+ $t = Title::newFromText( $title );
+ if ( is_null( $t ) )
+ return '';
+ return $t->getNamespace();
+ }
static function talkspace( $parser, $title = null ) {
$t = Title::newFromText( $title );
if ( is_null( $t ) || !$t->canTalk() )
@@ -461,6 +491,7 @@ class CoreParserFunctions {
/**
* Functions to get and normalize pagenames, corresponding to the magic words
* of the same names
+ * @return String
*/
static function pagename( $parser, $title = null ) {
$t = Title::newFromText( $title );
@@ -536,28 +567,64 @@ class CoreParserFunctions {
}
/**
- * Return the number of pages in the given category, or 0 if it's nonexis-
- * tent. This is an expensive parser function and can't be called too many
- * times per page.
+ * Return the number of pages, files or subcats in the given category,
+ * or 0 if it's nonexistent. This is an expensive parser function and
+ * can't be called too many times per page.
+ * @return string
*/
- static function pagesincategory( $parser, $name = '', $raw = null ) {
+ static function pagesincategory( $parser, $name = '', $arg1 = null, $arg2 = null ) {
+ static $magicWords = null;
+ if ( is_null( $magicWords ) ) {
+ $magicWords = new MagicWordArray( array(
+ 'pagesincategory_all',
+ 'pagesincategory_pages',
+ 'pagesincategory_subcats',
+ 'pagesincategory_files'
+ ) );
+ }
static $cache = array();
- $category = Category::newFromName( $name );
- if( !is_object( $category ) ) {
- $cache[$name] = 0;
+ // split the given option to its variable
+ if( self::isRaw( $arg1 ) ) {
+ //{{pagesincategory:|raw[|type]}}
+ $raw = $arg1;
+ $type = $magicWords->matchStartToEnd( $arg2 );
+ } else {
+ //{{pagesincategory:[|type[|raw]]}}
+ $type = $magicWords->matchStartToEnd( $arg1 );
+ $raw = $arg2;
+ }
+ if( !$type ) { //backward compatibility
+ $type = 'pagesincategory_all';
+ }
+
+ $title = Title::makeTitleSafe( NS_CATEGORY, $name );
+ if( !$title ) { # invalid title
return self::formatRaw( 0, $raw );
}
- # Normalize name for cache
- $name = $category->getName();
+ // Normalize name for cache
+ $name = $title->getDBkey();
- $count = 0;
- if( isset( $cache[$name] ) ) {
- $count = $cache[$name];
- } elseif( $parser->incrementExpensiveFunctionCount() ) {
- $count = $cache[$name] = (int)$category->getPageCount();
+ if( !isset( $cache[$name] ) ) {
+ $category = Category::newFromTitle( $title );
+
+ $allCount = $subcatCount = $fileCount = $pagesCount = 0;
+ if( $parser->incrementExpensiveFunctionCount() ) {
+ // $allCount is the total number of cat members,
+ // not the count of how many members are normal pages.
+ $allCount = (int)$category->getPageCount();
+ $subcatCount = (int)$category->getSubcatCount();
+ $fileCount = (int)$category->getFileCount();
+ $pagesCount = $allCount - $subcatCount - $fileCount;
+ }
+ $cache[$name]['pagesincategory_all'] = $allCount;
+ $cache[$name]['pagesincategory_pages'] = $pagesCount;
+ $cache[$name]['pagesincategory_subcats'] = $subcatCount;
+ $cache[$name]['pagesincategory_files'] = $fileCount;
}
+
+ $count = $cache[$name][$type];
return self::formatRaw( $count, $raw );
}
@@ -576,6 +643,7 @@ class CoreParserFunctions {
* @param $parser Parser
* @param $page String TODO DOCUMENT (Default: empty string)
* @param $raw TODO DOCUMENT (Default: null)
+ * @return string
*/
static function pagesize( $parser, $page = '', $raw = null ) {
static $cache = array();
@@ -593,7 +661,7 @@ class CoreParserFunctions {
if( isset( $cache[$page] ) ) {
$length = $cache[$page];
} elseif( $parser->incrementExpensiveFunctionCount() ) {
- $rev = Revision::newFromTitle( $title );
+ $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
$id = $rev ? $rev->getPage() : 0;
$length = $cache[$page] = $rev ? $rev->getSize() : 0;
@@ -605,7 +673,8 @@ class CoreParserFunctions {
/**
* Returns the requested protection level for the current page
- */
+ * @return string
+ */
static function protectionlevel( $parser, $type = '' ) {
$restrictions = $parser->mTitle->getRestrictions( strtolower( $type ) );
# Title::getRestrictions returns an array, its possible it may have
@@ -616,26 +685,20 @@ class CoreParserFunctions {
/**
* Gives language names.
* @param $parser Parser
- * @param $code String Language code
- * @param $language String Language code
+ * @param $code String Language code (of which to get name)
+ * @param $inLanguage String Language code (in which to get name)
* @return String
*/
- static function language( $parser, $code = '', $language = '' ) {
- global $wgContLang;
+ static function language( $parser, $code = '', $inLanguage = '' ) {
$code = strtolower( $code );
- $language = strtolower( $language );
-
- if ( $language !== '' ) {
- $names = Language::getTranslatedLanguageNames( $language );
- return isset( $names[$code] ) ? $names[$code] : wfBCP47( $code );
- }
-
- $lang = $wgContLang->getLanguageName( $code );
+ $inLanguage = strtolower( $inLanguage );
+ $lang = Language::fetchLanguageName( $code, $inLanguage );
return $lang !== '' ? $lang : wfBCP47( $code );
}
/**
* Unicode-safe str_pad with the restriction that $length is forced to be <= 500
+ * @return string
*/
static function pad( $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT ) {
$padding = $parser->killMarkers( $padding );
@@ -683,12 +746,16 @@ class CoreParserFunctions {
list( $page, $subpage ) = SpecialPageFactory::resolveAlias( $text );
if ( $page ) {
$title = SpecialPage::getTitleFor( $page, $subpage );
- return $title;
+ return $title->getPrefixedText();
} else {
- return wfMsgForContent( 'nosuchspecialpage' );
+ return wfMessage( 'nosuchspecialpage' )->inContentLanguage()->text();
}
}
+ static function speciale( $parser, $text ) {
+ return wfUrlencode( str_replace( ' ', '_', self::special( $parser, $text ) ) );
+ }
+
/**
* @param $parser Parser
* @param $text String The sortkey to use
@@ -716,48 +783,39 @@ class CoreParserFunctions {
return '';
} else {
return( '<span class="error">' .
- wfMsgForContent( 'duplicate-defaultsort',
- htmlspecialchars( $old ),
- htmlspecialchars( $text ) ) .
+ wfMessage( 'duplicate-defaultsort', $old, $text )->inContentLanguage()->escaped() .
'</span>' );
}
}
// Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}}
+ // or {{filepath|300px}}, {{filepath|200x300px}}, {{filepath|nowiki|200x300px}}, {{filepath|200x300px|nowiki}}
public static function filepath( $parser, $name='', $argA='', $argB='' ) {
$file = wfFindFile( $name );
- $size = '';
- $argA_int = intval( $argA );
- $argB_int = intval( $argB );
-
- if ( $argB_int > 0 ) {
- // {{filepath: | option | size }}
- $size = $argB_int;
- $option = $argA;
-
- } elseif ( $argA_int > 0 ) {
- // {{filepath: | size [|option] }}
- $size = $argA_int;
- $option = $argB;
+ if( $argA == 'nowiki' ) {
+ // {{filepath: | option [| size] }}
+ $isNowiki = true;
+ $parsedWidthParam = $parser->parseWidthParam( $argB );
} else {
- // {{filepath: [|option] }}
- $option = $argA;
+ // {{filepath: [| size [|option]] }}
+ $parsedWidthParam = $parser->parseWidthParam( $argA );
+ $isNowiki = ($argB == 'nowiki');
}
if ( $file ) {
$url = $file->getFullUrl();
// If a size is requested...
- if ( is_integer( $size ) ) {
- $mto = $file->transform( array( 'width' => $size ) );
+ if ( count( $parsedWidthParam ) ) {
+ $mto = $file->transform( $parsedWidthParam );
// ... and we can
if ( $mto && !$mto->isError() ) {
// ... change the URL to point to a thumbnail.
$url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
}
}
- if ( $option == 'nowiki' ) {
+ if ( $isNowiki ) {
return array( $url, 'nowiki' => true );
}
return $url;
@@ -768,6 +826,7 @@ class CoreParserFunctions {
/**
* Parser function to extension tag adaptor
+ * @return string
*/
public static function tagObj( $parser, $frame, $args ) {
if ( !count( $args ) ) {
@@ -784,7 +843,7 @@ class CoreParserFunctions {
$stripList = $parser->getStripList();
if ( !in_array( $tagName, $stripList ) ) {
return '<span class="error">' .
- wfMsgForContent( 'unknown_extension_tag', $tagName ) .
+ wfMessage( 'unknown_extension_tag', $tagName )->inContentLanguage()->text() .
'</span>';
}
diff --git a/includes/parser/CoreTagHooks.php b/includes/parser/CoreTagHooks.php
index 7d488c4b..296be66f 100644
--- a/includes/parser/CoreTagHooks.php
+++ b/includes/parser/CoreTagHooks.php
@@ -2,7 +2,23 @@
/**
* Tag hooks provided by MediaWiki core
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
diff --git a/includes/parser/DateFormatter.php b/includes/parser/DateFormatter.php
index 6559e886..2917b4a7 100644
--- a/includes/parser/DateFormatter.php
+++ b/includes/parser/DateFormatter.php
@@ -2,7 +2,23 @@
/**
* Date formatter
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -10,14 +26,15 @@
* @todo preferences, OutputPage
* @ingroup Parser
*/
-class DateFormatter
-{
+class DateFormatter {
var $mSource, $mTarget;
var $monthNames = '', $rxDM, $rxMD, $rxDMY, $rxYDM, $rxMDY, $rxYMD;
var $regexes, $pDays, $pMonths, $pYears;
var $rules, $xMonths, $preferences;
+ protected $lang;
+
const ALL = -1;
const NONE = 0;
const MDY = 1;
@@ -32,15 +49,15 @@ class DateFormatter
const LAST = 8;
/**
- * @todo document
+ * @param $lang Language In which language to format the date
*/
- function __construct() {
- global $wgContLang;
+ function __construct( Language $lang ) {
+ $this->lang = $lang;
$this->monthNames = $this->getMonthRegex();
for ( $i=1; $i<=12; $i++ ) {
- $this->xMonths[$wgContLang->lc( $wgContLang->getMonthName( $i ) )] = $i;
- $this->xMonths[$wgContLang->lc( $wgContLang->getMonthAbbreviation( $i ) )] = $i;
+ $this->xMonths[$this->lang->lc( $this->lang->getMonthName( $i ) )] = $i;
+ $this->xMonths[$this->lang->lc( $this->lang->getMonthAbbreviation( $i ) )] = $i;
}
$this->regexTrail = '(?![a-z])/iu';
@@ -103,16 +120,20 @@ class DateFormatter
/**
* Get a DateFormatter object
*
+ * @param $lang Language|string|null In which language to format the date
+ * Defaults to the site content language
* @return DateFormatter object
*/
- public static function &getInstance() {
- global $wgMemc;
+ public static function &getInstance( $lang = null ) {
+ global $wgMemc, $wgContLang;
static $dateFormatter = false;
+ $lang = $lang ? wfGetLangObj( $lang ) : $wgContLang;
+ $key = wfMemcKey( 'dateformatter', $lang->getCode() );
if ( !$dateFormatter ) {
- $dateFormatter = $wgMemc->get( wfMemcKey( 'dateformatter' ) );
+ $dateFormatter = $wgMemc->get( $key );
if ( !$dateFormatter ) {
- $dateFormatter = new DateFormatter;
- $wgMemc->set( wfMemcKey( 'dateformatter' ), $dateFormatter, 3600 );
+ $dateFormatter = new DateFormatter( $lang );
+ $wgMemc->set( $key, $dateFormatter, 3600 );
}
}
return $dateFormatter;
@@ -122,12 +143,12 @@ class DateFormatter
* @param $preference String: User preference
* @param $text String: Text to reformat
* @param $options Array: can contain 'linked' and/or 'match-whole'
+ * @return mixed|String
*/
function reformat( $preference, $text, $options = array('linked') ) {
-
$linked = in_array( 'linked', $options );
$match_whole = in_array( 'match-whole', $options );
-
+
if ( isset( $this->preferences[$preference] ) ) {
$preference = $this->preferences[$preference];
} else {
@@ -149,19 +170,19 @@ class DateFormatter
$this->mTarget = $i;
}
$regex = $this->regexes[$i];
-
+
// Horrible hack
if (!$linked) {
$regex = str_replace( array( '\[\[', '\]\]' ), '', $regex );
}
-
+
if ($match_whole) {
// Let's hope this works
$regex = preg_replace( '!^/!', '/^', $regex );
$regex = str_replace( $this->regexTrail,
'$'.$this->regexTrail, $regex );
}
-
+
// Another horrible hack
$this->mLinked = $linked;
$text = preg_replace_callback( $regex, array( &$this, 'replace' ), $text );
@@ -172,6 +193,7 @@ class DateFormatter
/**
* @param $matches
+ * @return string
*/
function replace( $matches ) {
# Extract information from $matches
@@ -186,10 +208,15 @@ class DateFormatter
$bits[$key[$p]] = $matches[$p+1];
}
}
-
+
return $this->formatDate( $bits, $linked );
}
-
+
+ /**
+ * @param $bits array
+ * @param $link bool
+ * @return string
+ */
function formatDate( $bits, $link = true ) {
$format = $this->targets[$this->mTarget];
@@ -203,13 +230,13 @@ class DateFormatter
# Construct new date
$text = '';
$fail = false;
-
+
// Pre-generate y/Y stuff because we need the year for the <span> title.
if ( !isset( $bits['y'] ) && isset( $bits['Y'] ) )
$bits['y'] = $this->makeIsoYear( $bits['Y'] );
if ( !isset( $bits['Y'] ) && isset( $bits['y'] ) )
$bits['Y'] = $this->makeNormalYear( $bits['y'] );
-
+
if ( !isset( $bits['m'] ) ) {
$m = $this->makeIsoMonth( $bits['F'] );
if ( !$m || $m == '00' ) {
@@ -218,7 +245,7 @@ class DateFormatter
$bits['m'] = $m;
}
}
-
+
if ( !isset($bits['d']) ) {
$bits['d'] = sprintf( '%02d', $bits['j'] );
}
@@ -248,8 +275,7 @@ class DateFormatter
if ( $m > 12 || $m < 1 ) {
$fail = true;
} else {
- global $wgContLang;
- $text .= $wgContLang->getMonthName( $m );
+ $text .= $this->lang->getMonthName( $m );
}
} else {
$text .= ucfirst( $bits['F'] );
@@ -265,30 +291,30 @@ class DateFormatter
if ( $fail ) {
$text = $matches[0];
}
-
+
$isoBits = array();
if ( isset($bits['y']) )
$isoBits[] = $bits['y'];
$isoBits[] = $bits['m'];
$isoBits[] = $bits['d'];
$isoDate = implode( '-', $isoBits );
-
+
// Output is not strictly HTML (it's wikitext), but <span> is whitelisted.
$text = Html::rawElement( 'span',
array( 'class' => 'mw-formatted-date', 'title' => $isoDate ), $text );
-
+
return $text;
}
/**
* @todo document
+ * @return string
*/
function getMonthRegex() {
- global $wgContLang;
$names = array();
for( $i = 1; $i <= 12; $i++ ) {
- $names[] = $wgContLang->getMonthName( $i );
- $names[] = $wgContLang->getMonthAbbreviation( $i );
+ $names[] = $this->lang->getMonthName( $i );
+ $names[] = $this->lang->getMonthAbbreviation( $i );
}
return implode( '|', $names );
}
@@ -299,9 +325,7 @@ class DateFormatter
* @return string ISO month name
*/
function makeIsoMonth( $monthName ) {
- global $wgContLang;
-
- $n = $this->xMonths[$wgContLang->lc( $monthName )];
+ $n = $this->xMonths[$this->lang->lc( $monthName )];
return sprintf( '%02d', $n );
}
@@ -325,6 +349,7 @@ class DateFormatter
/**
* @todo document
+ * @return int|string
*/
function makeNormalYear( $iso ) {
if ( $iso[0] == '-' ) {
diff --git a/includes/parser/LinkHolderArray.php b/includes/parser/LinkHolderArray.php
index fb013047..d9356b48 100644
--- a/includes/parser/LinkHolderArray.php
+++ b/includes/parser/LinkHolderArray.php
@@ -2,7 +2,23 @@
/**
* Holder of replacement pairs for wiki links
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -33,7 +49,8 @@ class LinkHolderArray {
* serializing at present.
*
* Compact the titles, only serialize the text form.
- */
+ * @return array
+ */
function __sleep() {
foreach ( $this->internals as &$nsLinks ) {
foreach ( $nsLinks as &$entry ) {
@@ -134,6 +151,7 @@ class LinkHolderArray {
/**
* Get a subset of the current LinkHolderArray which is sufficient to
* interpret the given text.
+ * @return LinkHolderArray
*/
function getSubArray( $text ) {
$sub = new LinkHolderArray( $this->parent );
@@ -167,6 +185,7 @@ class LinkHolderArray {
/**
* Returns true if the memory requirements of this object are getting large
+ * @return bool
*/
function isBig() {
global $wgLinkHolderBatchSize;
@@ -190,6 +209,11 @@ class LinkHolderArray {
* article length checks (for stub links) to be bundled into a single query.
*
* @param $nt Title
+ * @param $text String
+ * @param $query Array [optional]
+ * @param $trail String [optional]
+ * @param $prefix String [optional]
+ * @return string
*/
function makeHolder( $nt, $text = '', $query = array(), $trail = '', $prefix = '' ) {
wfProfileIn( __METHOD__ );
@@ -433,7 +457,7 @@ class LinkHolderArray {
foreach ( $entries as $index => $entry ) {
$pdbk = $entry['pdbk'];
// we only deal with new links (in its first query)
- if ( !isset( $colours[$pdbk] ) ) {
+ if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) {
$title = $entry['title'];
$titleText = $title->getText();
$titlesAttrs[] = array(
@@ -449,7 +473,7 @@ class LinkHolderArray {
}
// Now do the conversion and explode string to text of titles
- $titlesAllVariants = $wgContLang->autoConvertToAllVariants( $titlesToBeConverted );
+ $titlesAllVariants = $wgContLang->autoConvertToAllVariants( rtrim( $titlesToBeConverted, "\0" ) );
$allVariantsName = array_keys( $titlesAllVariants );
foreach ( $titlesAllVariants as &$titlesVariant ) {
$titlesVariant = explode( "\0", $titlesVariant );
@@ -517,7 +541,7 @@ class LinkHolderArray {
$entry =& $this->internals[$ns][$index];
$pdbk = $entry['pdbk'];
- if(!isset($colours[$pdbk])){
+ if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) {
// found link in some of the variants, replace the link holder data
$entry['title'] = $variantTitle;
$entry['pdbk'] = $varPdbk;
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index d4f167c9..2a24bee7 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -1,12 +1,29 @@
<?php
/**
- * @defgroup Parser Parser
+ * PHP parser that converts wiki markup to HTML.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Parser
- * File for Parser and related classes
*/
+/**
+ * @defgroup Parser Parser
+ */
/**
* PHP Parser - Processes wiki markup (which uses a more user-friendly
@@ -14,36 +31,38 @@
* transformation of that wiki markup it into XHTML output / markup
* (which in turn the browser understands, and can display).
*
- * <pre>
- * There are five main entry points into the Parser class:
- * parse()
+ * There are seven main entry points into the Parser class:
+ *
+ * - Parser::parse()
* produces HTML output
- * preSaveTransform().
+ * - Parser::preSaveTransform().
* produces altered wiki markup.
- * preprocess()
+ * - Parser::preprocess()
* removes HTML comments and expands templates
- * cleanSig() / cleanSigInSig()
+ * - Parser::cleanSig() and Parser::cleanSigInSig()
* Cleans a signature before saving it to preferences
- * getSection()
+ * - Parser::getSection()
* Return the content of a section from an article for section editing
- * replaceSection()
+ * - Parser::replaceSection()
* Replaces a section by number inside an article
- * getPreloadText()
+ * - Parser::getPreloadText()
* Removes <noinclude> sections, and <includeonly> tags.
*
* Globals used:
* object: $wgContLang
*
- * NOT $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
+ * @warning $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
*
- * settings:
- * $wgUseDynamicDates*, $wgInterwikiMagic*,
- * $wgNamespacesWithSubpages, $wgAllowExternalImages*,
- * $wgLocaltimezone, $wgAllowSpecialInclusion*,
- * $wgMaxArticleSize*
+ * @par Settings:
+ * $wgLocaltimezone
+ * $wgNamespacesWithSubpages
*
- * * only within ParserOptions
- * </pre>
+ * @par Settings only within ParserOptions:
+ * $wgAllowExternalImages
+ * $wgAllowSpecialInclusion
+ * $wgInterwikiMagic
+ * $wgMaxArticleSize
+ * $wgUseDynamicDates
*
* @ingroup Parser
*/
@@ -144,7 +163,8 @@ class Parser {
var $mLinkHolders;
var $mLinkID;
- var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
+ var $mIncludeSizes, $mPPNodeCount, $mGeneratedPPNodeCount, $mHighestExpansionDepth;
+ var $mDefaultSort;
var $mTplExpandCache; # empty-frame expansion cache
var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
var $mExpensiveFunctionCount; # number of expensive parser function calls
@@ -188,7 +208,7 @@ class Parser {
public function __construct( $conf = array() ) {
$this->mConf = $conf;
$this->mUrlProtocols = wfUrlProtocols();
- $this->mExtLinkBracketedRegex = '/\[((' . wfUrlProtocols() . ')'.
+ $this->mExtLinkBracketedRegex = '/\[(((?i)' . $this->mUrlProtocols . ')'.
self::EXT_LINK_URL_CLASS.'+)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/Su';
if ( isset( $conf['preprocessorClass'] ) ) {
$this->mPreprocessorClass = $conf['preprocessorClass'];
@@ -273,8 +293,6 @@ class Parser {
* Must not consist of all title characters, or else it will change
* the behaviour of <nowiki> in a link.
*/
- # $this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString();
- # Changed to \x7f to allow XML double-parsing -- TS
$this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString();
$this->mStripState = new StripState( $this->mUniqPrefix );
@@ -289,6 +307,8 @@ class Parser {
'arg' => 0,
);
$this->mPPNodeCount = 0;
+ $this->mGeneratedPPNodeCount = 0;
+ $this->mHighestExpansionDepth = 0;
$this->mDefaultSort = false;
$this->mHeadings = array();
$this->mDoubleUnderscores = array();
@@ -321,13 +341,18 @@ class Parser {
* to internalParse() which does all the real work.
*/
- global $wgUseTidy, $wgAlwaysUseTidy, $wgDisableLangConversion, $wgDisableTitleConversion;
+ global $wgUseTidy, $wgAlwaysUseTidy;
$fname = __METHOD__.'-' . wfGetCaller();
wfProfileIn( __METHOD__ );
wfProfileIn( $fname );
$this->startParse( $title, $options, self::OT_HTML, $clearState );
+ # 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;
@@ -343,6 +368,7 @@ class Parser {
# 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 );
@@ -368,9 +394,8 @@ class Parser {
* 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() ) )
+ if ( !( $options->getDisableContentConversion()
+ || isset( $this->mDoubleUnderscores['nocontentconvert'] ) ) )
{
# Run convert unconditionally in 1.18-compatible mode
global $wgBug34832TransitionalRollback;
@@ -389,8 +414,7 @@ class Parser {
* {{DISPLAYTITLE:...}} is present. DISPLAYTITLE takes precedence over
* automatic link conversion.
*/
- if ( !( $wgDisableLangConversion
- || $wgDisableTitleConversion
+ if ( !( $options->getDisableTitleConversion()
|| isset( $this->mDoubleUnderscores['nocontentconvert'] )
|| isset( $this->mDoubleUnderscores['notitleconvert'] )
|| $this->mOutput->getDisplayTitle() !== false ) )
@@ -442,9 +466,12 @@ class Parser {
array_values( $tidyregs ),
$text );
}
- global $wgExpensiveParserFunctionLimit;
- if ( $this->mExpensiveFunctionCount > $wgExpensiveParserFunctionLimit ) {
- $this->limitationWarn( 'expensive-parserfunction', $this->mExpensiveFunctionCount, $wgExpensiveParserFunctionLimit );
+
+ if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
+ $this->limitationWarn( 'expensive-parserfunction',
+ $this->mExpensiveFunctionCount,
+ $this->mOptions->getExpensiveParserFunctionLimit()
+ );
}
wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
@@ -452,12 +479,15 @@ class Parser {
# Information on include size limits, for the benefit of users who try to skirt them
if ( $this->mOptions->getEnableLimitReport() ) {
$max = $this->mOptions->getMaxIncludeSize();
- $PFreport = "Expensive parser function count: {$this->mExpensiveFunctionCount}/$wgExpensiveParserFunctionLimit\n";
+ $PFreport = "Expensive parser function count: {$this->mExpensiveFunctionCount}/{$this->mOptions->getExpensiveParserFunctionLimit()}\n";
$limitReport =
"NewPP limit report\n" .
- "Preprocessor node count: {$this->mPPNodeCount}/{$this->mOptions->getMaxPPNodeCount()}\n" .
+ "Preprocessor visited node count: {$this->mPPNodeCount}/{$this->mOptions->getMaxPPNodeCount()}\n" .
+ "Preprocessor generated node count: " .
+ "{$this->mGeneratedPPNodeCount}/{$this->mOptions->getMaxGeneratedPPNodeCount()}\n" .
"Post-expand include size: {$this->mIncludeSizes['post-expand']}/$max bytes\n" .
"Template argument size: {$this->mIncludeSizes['arg']}/$max bytes\n".
+ "Highest expansion depth: {$this->mHighestExpansionDepth}/{$this->mOptions->getMaxPPExpandDepth()}\n".
$PFreport;
wfRunHooks( 'ParserLimitReport', array( $this, &$limitReport ) );
$text .= "\n<!-- \n$limitReport-->\n";
@@ -497,6 +527,7 @@ class Parser {
/**
* Expand templates and variables in the text, producing valid, static wikitext.
* Also removes comments.
+ * @return mixed|string
*/
function preprocess( $text, Title $title, ParserOptions $options, $revid = null ) {
wfProfileIn( __METHOD__ );
@@ -530,10 +561,11 @@ class Parser {
}
/**
- * Process the wikitext for the ?preload= feature. (bug 5210)
+ * 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.
+ * "<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
@@ -557,7 +589,7 @@ class Parser {
* @return string
*/
static public function getRandomString() {
- return dechex( mt_rand( 0, 0x7fffffff ) ) . dechex( mt_rand( 0, 0x7fffffff ) );
+ return wfRandomString( 16 );
}
/**
@@ -619,7 +651,7 @@ class Parser {
/**
* Accessor/mutator for the Title object
*
- * @param $x New Title object or null to just get the current one
+ * @param $x Title object or null to just get the current one
* @return Title object
*/
function Title( $x = null ) {
@@ -645,7 +677,7 @@ class Parser {
/**
* Accessor/mutator for the output type
*
- * @param $x New value or null to just get the current one
+ * @param $x int|null New value or null to just get the current one
* @return Integer
*/
function OutputType( $x = null ) {
@@ -673,8 +705,8 @@ class Parser {
/**
* Accessor/mutator for the ParserOptions object
*
- * @param $x New value or null to just get the current one
- * @return Current ParserOptions object
+ * @param $x ParserOptions New value or null to just get the current one
+ * @return ParserOptions Current ParserOptions object
*/
function Options( $x = null ) {
return wfSetVar( $this->mOptions, $x );
@@ -703,18 +735,24 @@ class Parser {
}
/**
- * Get the target language for the content being parsed. This is usually the
- * language that the content is in.
+ * Get the target language for the content being parsed. This is usually the
+ * language that the content is in.
+ *
+ * @since 1.19
+ *
+ * @return Language|null
*/
- function getTargetLanguage() {
+ public function getTargetLanguage() {
$target = $this->mOptions->getTargetLanguage();
+
if ( $target !== null ) {
return $target;
} elseif( $this->mOptions->getInterfaceMessage() ) {
return $this->mOptions->getUserLangObj();
} elseif( is_null( $this->mTitle ) ) {
- throw new MWException( __METHOD__.': $this->mTitle is null' );
+ throw new MWException( __METHOD__ . ': $this->mTitle is null' );
}
+
return $this->mTitle->getPageLanguage();
}
@@ -761,11 +799,14 @@ class Parser {
* in the text with a random marker and returns the next text. The output
* parameter $matches will be an associative array filled with data in
* the form:
+ *
+ * @code
* 'UNIQ-xxxxx' => array(
* 'element',
* 'tag content',
* array( 'param' => 'x' ),
* '<element param="x">tag content</element>' ) )
+ * @endcode
*
* @param $elements array list of element names. Comments are always extracted.
* @param $text string Source text string.
@@ -864,6 +905,7 @@ class Parser {
* parse the wiki syntax used to render tables
*
* @private
+ * @return string
*/
function doTableStuff( $text ) {
wfProfileIn( __METHOD__ );
@@ -1094,6 +1136,7 @@ class Parser {
$text = $this->replaceVariables( $text );
}
+ wfRunHooks( 'InternalParseBeforeSanitize', array( &$this, &$text, &$this->mStripState ) );
$text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), false, array_keys( $this->mTransparentTagHooks ) );
wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
@@ -1146,7 +1189,7 @@ class Parser {
'!(?: # Start cases
(<a[ \t\r\n>].*?</a>) | # m[1]: Skip link text
(<.*?>) | # m[2]: Skip stuff inside HTML elements' . "
- (\\b(?:$prots)$urlChar+) | # m[3]: Free external links" . '
+ (\\b(?i:$prots)$urlChar+) | # m[3]: Free external links" . '
(?:RFC|PMID)\s+([0-9]+) | # m[4]: RFC or PMID, capture number
ISBN\s+(\b # m[5]: ISBN, capture number
(?: 97[89] [\ \-]? )? # optional 13-digit ISBN prefix
@@ -1189,7 +1232,7 @@ class Parser {
throw new MWException( __METHOD__.': unrecognised match type "' .
substr( $m[0], 0, 20 ) . '"' );
}
- $url = wfMsgForContent( $urlmsg, $id );
+ $url = wfMessage( $urlmsg, $id )->inContentLanguage()->text();
return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $CssClass );
} elseif ( isset( $m[5] ) && $m[5] !== '' ) {
# ISBN
@@ -1249,7 +1292,7 @@ class Parser {
$text = $this->maybeMakeExternalImage( $url );
if ( $text === false ) {
# Not an image, make a link
- $text = Linker::makeExternalLink( $url,
+ $text = Linker::makeExternalLink( $url,
$this->getConverterLanguage()->markNoConversion($url), true, 'free',
$this->getExternalLinkAttribs( $url ) );
# Register it in the output object...
@@ -1646,7 +1689,7 @@ class Parser {
}
if ( !$text && $this->mOptions->getEnableImageWhitelist()
&& preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
- $whitelist = explode( "\n", wfMsgForContent( 'external_image_whitelist' ) );
+ $whitelist = explode( "\n", wfMessage( 'external_image_whitelist' )->inContentLanguage()->text() );
foreach ( $whitelist as $entry ) {
# Sanitize the regex fragment, make it case-insensitive, ignore blank entries/comments
if ( strpos( $entry, '#' ) === 0 || $entry === '' ) {
@@ -1698,7 +1741,7 @@ class Parser {
$holders = new LinkHolderArray( $this );
- # split the entire text string on occurences of [[
+ # split the entire text string on occurrences of [[
$a = StringUtils::explode( '[[', ' ' . $s );
# get the first element (all text up to first [[), and remove the space we added
$s = $a->current();
@@ -1711,7 +1754,7 @@ class Parser {
if ( $useLinkPrefixExtension ) {
# Match the end of a line for a word that's not followed by whitespace,
# e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
- $e2 = wfMsgForContent( 'linkprefix' );
+ $e2 = wfMessage( 'linkprefix' )->inContentLanguage()->text();
}
if ( is_null( $this->mTitle ) ) {
@@ -1733,7 +1776,7 @@ class Parser {
}
if ( $this->getConverterLanguage()->hasVariants() ) {
- $selflink = $this->getConverterLanguage()->autoConvertToAllVariants(
+ $selflink = $this->getConverterLanguage()->autoConvertToAllVariants(
$this->mTitle->getPrefixedText() );
} else {
$selflink = array( $this->mTitle->getPrefixedText() );
@@ -1812,7 +1855,7 @@ class Parser {
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
# should be external links.
- if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $m[1] ) ) {
+ if ( preg_match( '/^(?i:' . $this->mUrlProtocols . ')/', $m[1] ) ) {
$s .= $prefix . '[[' . $line ;
wfProfileOut( __METHOD__."-misc" );
continue;
@@ -1902,11 +1945,9 @@ class Parser {
# Link not escaped by : , create the various objects
if ( $noforce ) {
- global $wgContLang;
-
# Interwikis
wfProfileIn( __METHOD__."-interwiki" );
- if ( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
+ if ( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && Language::fetchLanguageName( $iw, null, 'mw' ) ) {
$this->mOutput->addLanguageLink( $nt->getFullText() );
$s = rtrim( $s . $prefix );
$s .= trim( $trail, "\n" ) == '' ? '': $prefix . $trail;
@@ -2051,7 +2092,7 @@ class Parser {
* @return String: less-or-more HTML with NOPARSE bits
*/
function armorLinks( $text ) {
- return preg_replace( '/\b(' . wfUrlProtocols() . ')/',
+ return preg_replace( '/\b((?i)' . $this->mUrlProtocols . ')/',
"{$this->mUniqPrefix}NOPARSE$1", $text );
}
@@ -2122,7 +2163,7 @@ class Parser {
* element appropriate to the prefix character passed into them.
* @private
*
- * @param $char char
+ * @param $char string
*
* @return string
*/
@@ -2384,7 +2425,7 @@ class Parser {
}
/**
- * Split up a string on ':', ignoring any occurences inside tags
+ * Split up a string on ':', ignoring any occurrences inside tags
* to prevent illegal overlapping.
*
* @param $str String the string to split
@@ -2698,6 +2739,18 @@ class Parser {
$subjPage = $this->mTitle->getSubjectPage();
$value = wfEscapeWikiText( $subjPage->getPrefixedUrl() );
break;
+ case 'pageid': // requested in bug 23427
+ $pageid = $this->getTitle()->getArticleId();
+ if( $pageid == 0 ) {
+ # 0 means the page doesn't exist in the database,
+ # which means the user is previewing a new page.
+ # The vary-revision flag must be set, because the magic word
+ # will have a different value once the page is saved.
+ $this->mOutput->setFlag( 'vary-revision' );
+ wfDebug( __METHOD__ . ": {{PAGEID}} used in a new page, setting vary-revision...\n" );
+ }
+ $value = $pageid ? $pageid : null;
+ break;
case 'revisionid':
# Let the edit saving system know we should parse the page
# *after* a revision ID has been assigned.
@@ -2760,6 +2813,9 @@ class Parser {
case 'namespacee':
$value = wfUrlencode( $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
break;
+ case 'namespacenumber':
+ $value = $this->mTitle->getNamespace();
+ break;
case 'talkspace':
$value = $this->mTitle->canTalk() ? str_replace( '_',' ',$this->mTitle->getTalkNsText() ) : '';
break;
@@ -2834,7 +2890,8 @@ class Parser {
$value = $pageLang->formatNum( SiteStats::edits() );
break;
case 'numberofviews':
- $value = $pageLang->formatNum( SiteStats::views() );
+ global $wgDisableCounters;
+ $value = !$wgDisableCounters ? $pageLang->formatNum( SiteStats::views() ) : '';
break;
case 'currenttimestamp':
$value = wfTimestamp( TS_MW, $ts );
@@ -2900,7 +2957,7 @@ class Parser {
*
* @param $text String: The text to parse
* @param $flags Integer: bitwise combination of:
- * self::PTD_FOR_INCLUSION Handle <noinclude>/<includeonly> as if the text is being
+ * self::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>" as if the text is being
* included. Default is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
@@ -3027,13 +3084,14 @@ class Parser {
* 'post-expand-template-inclusion' (corresponding messages:
* 'post-expand-template-inclusion-warning',
* 'post-expand-template-inclusion-category')
- * @param $current Current value
- * @param $max Maximum allowed, when an explicit limit has been
+ * @param $current int|null Current value
+ * @param $max int|null Maximum allowed, when an explicit limit has been
* exceeded, provide the values (optional)
*/
- function limitationWarn( $limitationType, $current=null, $max=null) {
+ function limitationWarn( $limitationType, $current = '', $max = '' ) {
# does no harm if $current and $max are present but are unnecessary for the message
- $warning = wfMsgExt( "$limitationType-warning", array( 'parsemag', 'escape' ), $current, $max );
+ $warning = wfMessage( "$limitationType-warning" )->numParams( $current, $max )
+ ->inContentLanguage()->escaped();
$this->mOutput->addWarning( $warning );
$this->addTrackingCategory( "$limitationType-category" );
}
@@ -3051,7 +3109,7 @@ class Parser {
* @private
*/
function braceSubstitution( $piece, $frame ) {
- global $wgNonincludableNamespaces, $wgContLang;
+ global $wgContLang;
wfProfileIn( __METHOD__ );
wfProfileIn( __METHOD__.'-setup' );
@@ -3238,7 +3296,8 @@ class Parser {
if ( $frame->depth >= $limit ) {
$found = true;
$text = '<span class="error">'
- . wfMsgForContent( 'parser-template-recursion-depth-warning', $limit )
+ . wfMessage( 'parser-template-recursion-depth-warning' )
+ ->numParams( $limit )->inContentLanguage()->text()
. '</span>';
}
}
@@ -3246,8 +3305,11 @@ class Parser {
# Load from database
if ( !$found && $title ) {
- $titleProfileIn = __METHOD__ . "-title-" . $title->getDBKey();
- wfProfileIn( $titleProfileIn ); // template in
+ if ( !Profiler::instance()->isPersistent() ) {
+ # Too many unique items can kill profiling DBs/collectors
+ $titleProfileIn = __METHOD__ . "-title-" . $title->getDBKey();
+ wfProfileIn( $titleProfileIn ); // template in
+ }
wfProfileIn( __METHOD__ . '-loadtpl' );
if ( !$title->isExternal() ) {
if ( $title->isSpecialPage()
@@ -3281,7 +3343,7 @@ class Parser {
$isHTML = true;
$this->disableCache();
}
- } elseif ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
+ } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
$found = false; # access denied
wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() );
} else {
@@ -3315,7 +3377,9 @@ class Parser {
# This has to be done after redirect resolution to avoid infinite loops via redirects
if ( !$frame->loopCheck( $title ) ) {
$found = true;
- $text = '<span class="error">' . wfMsgForContent( 'parser-template-loop-warning', $titleText ) . '</span>';
+ $text = '<span class="error">'
+ . wfMessage( 'parser-template-loop-warning', $titleText )->inContentLanguage()->text()
+ . '</span>';
wfDebug( __METHOD__.": template loop broken at '$titleText'\n" );
}
wfProfileOut( __METHOD__ . '-loadtpl' );
@@ -3362,10 +3426,8 @@ class Parser {
}
# Replace raw HTML by a placeholder
- # Add a blank line preceding, to prevent it from mucking up
- # immediately preceding headings
if ( $isHTML ) {
- $text = "\n\n" . $this->insertStripItem( $text );
+ $text = $this->insertStripItem( $text );
} elseif ( $nowiki && ( $this->ot['html'] || $this->ot['pre'] ) ) {
# Escape nowiki-style return values
$text = wfEscapeWikiText( $text );
@@ -3476,7 +3538,7 @@ class Parser {
* Static function to get a template
* Can be overridden via ParserOptions::setTemplateCallback().
*
- * @parma $title Title
+ * @param $title Title
* @param $parser Parser
*
* @return array
@@ -3505,7 +3567,7 @@ class Parser {
# Get the revision
$rev = $id
? Revision::newFromId( $id )
- : Revision::newFromTitle( $title );
+ : Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
$rev_id = $rev ? $rev->getId() : 0;
# If there is no current revision, there is no page
if ( $id === false && !$rev ) {
@@ -3556,7 +3618,7 @@ class Parser {
* If 'broken' is a key in $options then the file will appear as a broken thumbnail.
* @param Title $title
* @param Array $options Array of options to RepoGroup::findFile
- * @return File|false
+ * @return File|bool
*/
function fetchFile( $title, $options = array() ) {
$res = $this->fetchFileAndTitle( $title, $options );
@@ -3607,13 +3669,13 @@ class Parser {
global $wgEnableScaryTranscluding;
if ( !$wgEnableScaryTranscluding ) {
- return wfMsgForContent('scarytranscludedisabled');
+ return wfMessage('scarytranscludedisabled')->inContentLanguage()->text();
}
$url = $title->getFullUrl( "action=$action" );
if ( strlen( $url ) > 255 ) {
- return wfMsgForContent( 'scarytranscludetoolong' );
+ return wfMessage( 'scarytranscludetoolong' )->inContentLanguage()->text();
}
return $this->fetchScaryTemplateMaybeFromCache( $url );
}
@@ -3634,7 +3696,7 @@ class Parser {
$text = Http::get( $url );
if ( !$text ) {
- return wfMsgForContent( 'scarytranscludefailed', $url );
+ return wfMessage( 'scarytranscludefailed', $url )->inContentLanguage()->text();
}
$dbw = wfGetDB( DB_MASTER );
@@ -3650,7 +3712,7 @@ class Parser {
* Triple brace replacement -- used for template arguments
* @private
*
- * @param $peice array
+ * @param $piece array
* @param $frame PPFrame
*
* @return array
@@ -3700,7 +3762,7 @@ class Parser {
* Return the text to be used for a given extension tag.
* This is the ghost of strip().
*
- * @param $params Associative array of parameters:
+ * @param $params array Associative array of parameters:
* name PPNode for the tag name
* attr PPNode for unparsed text where tag attributes are thought to be
* attributes Optional associative array of parsed attributes
@@ -3808,12 +3870,8 @@ class Parser {
* @return Boolean: false if the limit has been exceeded
*/
function incrementExpensiveFunctionCount() {
- global $wgExpensiveParserFunctionLimit;
$this->mExpensiveFunctionCount++;
- if ( $this->mExpensiveFunctionCount <= $wgExpensiveParserFunctionLimit ) {
- return true;
- }
- return false;
+ return $this->mExpensiveFunctionCount <= $this->mOptions->getExpensiveParserFunctionLimit();
}
/**
@@ -3921,6 +3979,7 @@ class Parser {
* @param $text String
* @param $origText String: original, untouched wikitext
* @param $isMain Boolean
+ * @return mixed|string
* @private
*/
function formatHeadings( $text, $origText, $isMain=true ) {
@@ -4147,7 +4206,7 @@ class Parser {
# Don't number the heading if it is the only one (looks silly)
if ( count( $matches[3] ) > 1 && $this->mOptions->getNumberHeadings() ) {
# the two are different if the line contains a link
- $headline = $numbering . ' ' . $headline;
+ $headline = Html::element( 'span', array( 'class' => 'mw-headline-number' ), $numbering ) . ' ' . $headline;
}
# Create the anchor for linking from the TOC to the section
@@ -4286,7 +4345,7 @@ class Parser {
}
/**
- * Transform wiki markup when saving a page by doing \r\n -> \n
+ * Transform wiki markup when saving a page by doing "\r\n" -> "\n"
* conversion, substitting signatures, {{subst:}} templates, etc.
*
* @param $text String: the text to transform
@@ -4362,7 +4421,7 @@ class Parser {
$text = $this->replaceVariables( $text );
# This works almost by chance, as the replaceVariables are done before the getUserSig(),
- # which may corrupt this parser instance via its wfMsgExt( parsemag ) call-
+ # which may corrupt this parser instance via its wfMessage()->text() call-
# Signatures
$sigText = $this->getUserSig( $user );
@@ -4373,13 +4432,12 @@ class Parser {
) );
# Context links: [[|name]] and [[name (context)|]]
- global $wgLegalTitleChars;
- $tc = "[$wgLegalTitleChars]";
+ $tc = '[' . Title::legalChars() . ']';
$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|]]
+ $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]]"
@@ -4583,7 +4641,7 @@ class Parser {
}
/**
- * Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
+ * Create an HTML-style tag, e.g. "<yourtag>special text</yourtag>"
* The callback should have the following form:
* function myParserHook( $text, $params, $parser, $frame ) { ... }
*
@@ -4601,13 +4659,15 @@ class Parser {
* 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 $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 Mixed|null The old value of the mTagHooks array associated with the hook
*/
public function setHook( $tag, $callback ) {
$tag = strtolower( $tag );
- if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setHook('$tag', ...) call" );
+ if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) {
+ throw new MWException( "Invalid character {$m[0]} in setHook('$tag', ...) call" );
+ }
$oldVal = isset( $this->mTagHooks[$tag] ) ? $this->mTagHooks[$tag] : null;
$this->mTagHooks[$tag] = $callback;
if ( !in_array( $tag, $this->mStripList ) ) {
@@ -4629,13 +4689,15 @@ class Parser {
* @since 1.10
* @todo better document or deprecate this
*
- * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+ * @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 Mixed|null 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" );
+ if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) {
+ throw new MWException( "Invalid character {$m[0]} in setTransparentHook('$tag', ...) call" );
+ }
$oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
$this->mTransparentTagHooks[$tag] = $callback;
@@ -4691,7 +4753,7 @@ class Parser {
* Please read the documentation in includes/parser/Preprocessor.php for more information
* about the methods available in PPFrame and PPNode.
*
- * @return The old callback function for this name, if any
+ * @return string|callback The old callback function for this name, if any
*/
public function setFunctionHook( $id, $callback, $flags = 0 ) {
global $wgContLang;
@@ -4735,9 +4797,10 @@ class Parser {
}
/**
- * Create a tag function, e.g. <test>some stuff</test>.
+ * Create a tag function, e.g. "<test>some stuff</test>".
* Unlike tag hooks, tag functions are parsed at preprocessor level.
* Unlike parser functions, their content is not preprocessed.
+ * @return null
*/
function setFunctionTagHook( $tag, $callback, $flags ) {
$tag = strtolower( $tag );
@@ -4755,7 +4818,7 @@ class Parser {
/**
* @todo FIXME: Update documentation. makeLinkObj() is deprecated.
- * Replace <!--LINK--> link placeholders with actual links, in the buffer
+ * Replace "<!--LINK-->" link placeholders with actual links, in the buffer
* Placeholders created in Skin::makeLinkObj()
*
* @param $text string
@@ -4768,7 +4831,7 @@ class Parser {
}
/**
- * Replace <!--LINK--> link placeholders with plain text of links
+ * Replace "<!--LINK-->" link placeholders with plain text of links
* (not HTML-formatted).
*
* @param $text String
@@ -4845,30 +4908,41 @@ class Parser {
$label = '';
$alt = '';
+ $link = '';
if ( isset( $matches[3] ) ) {
// 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]);
+ $parameterMatches = StringUtils::explode('|', $matches[3]);
$magicWordAlt = MagicWord::get( 'img_alt' );
+ $magicWordLink = MagicWord::get( 'img_link' );
- foreach ( $altmatches as $altmatch ) {
- $match = $magicWordAlt->matchVariableStartToEnd( $altmatch );
- if ( $match ) {
+ foreach ( $parameterMatches as $parameterMatch ) {
+ if ( $match = $magicWordAlt->matchVariableStartToEnd( $parameterMatch ) ) {
$alt = $this->stripAltText( $match, false );
}
+ elseif( $match = $magicWordLink->matchVariableStartToEnd( $parameterMatch ) ){
+ $link = strip_tags($this->replaceLinkHoldersText($match));
+ $chars = self::EXT_LINK_URL_CLASS;
+ $prots = $this->mUrlProtocols;
+ //check to see if link matches an absolute url, if not then it must be a wiki link.
+ if(!preg_match( "/^($prots)$chars+$/u", $link)){
+ $localLinkTitle = Title::newFromText($link);
+ $link = $localLinkTitle->getLocalURL();
+ }
+ }
else {
// concatenate all other pipes
- $label .= '|' . $altmatch;
+ $label .= '|' . $parameterMatch;
}
}
// remove the first pipe
$label = substr( $label, 1 );
}
- $ig->add( $title, $label, $alt );
+ $ig->add( $title, $label, $alt ,$link);
}
return $ig->toHTML();
}
@@ -4890,7 +4964,7 @@ class Parser {
'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle',
'bottom', 'text-bottom' ),
'frame' => array( 'thumbnail', 'manualthumb', 'framed', 'frameless',
- 'upright', 'border', 'link', 'alt' ),
+ 'upright', 'border', 'link', 'alt', 'class' ),
);
static $internalParamMap;
if ( !$internalParamMap ) {
@@ -4922,7 +4996,7 @@ class Parser {
*
* @param $title Title
* @param $options String
- * @param $holders LinkHolderArray|false
+ * @param $holders LinkHolderArray|bool
* @return string HTML
*/
function makeImage( $title, $options, $holders = false ) {
@@ -4940,6 +5014,7 @@ class Parser {
# * upright reduce width for upright images, rounded to full __0 px
# * border draw a 1px border around the image
# * alt Text for HTML alt attribute (defaults to empty)
+ # * class Set a class for img node
# * link Set the target of the image link. Can be external, interwiki, or local
# vertical-align values (no % or length right now):
# * baseline
@@ -4983,27 +5058,22 @@ class Parser {
# Special case; width and height come in one variable together
if ( $type === 'handler' && $paramName === 'width' ) {
- $m = array();
- # (bug 13500) In both cases (width/height and width only),
- # permit trailing "px" for backward compatibility.
- if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
- $width = intval( $m[1] );
- $height = intval( $m[2] );
+ $parsedWidthParam = $this->parseWidthParam( $value );
+ if( isset( $parsedWidthParam['width'] ) ) {
+ $width = $parsedWidthParam['width'];
if ( $handler->validateParam( 'width', $width ) ) {
$params[$type]['width'] = $width;
$validated = true;
}
+ }
+ if( isset( $parsedWidthParam['height'] ) ) {
+ $height = $parsedWidthParam['height'];
if ( $handler->validateParam( 'height', $height ) ) {
$params[$type]['height'] = $height;
$validated = true;
}
- } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
- $width = intval( $value );
- if ( $handler->validateParam( 'width', $width ) ) {
- $params[$type]['width'] = $width;
- $validated = true;
- }
- } # else no validation -- bug 13436
+ }
+ # else no validation -- bug 13436
} else {
if ( $type === 'handler' ) {
# Validate handler parameter
@@ -5013,6 +5083,7 @@ class Parser {
switch( $paramName ) {
case 'manualthumb':
case 'alt':
+ case 'class':
# @todo FIXME: Possibly check validity here for
# manualthumb? downstream behavior seems odd with
# missing manual thumbs.
@@ -5026,8 +5097,8 @@ class Parser {
$paramName = 'no-link';
$value = true;
$validated = true;
- } elseif ( preg_match( "/^$prots/", $value ) ) {
- if ( preg_match( "/^($prots)$chars+$/u", $value, $m ) ) {
+ } elseif ( preg_match( "/^(?i)$prots/", $value ) ) {
+ if ( preg_match( "/^((?i)$prots)$chars+$/u", $value, $m ) ) {
$paramName = 'link-url';
$this->mOutput->addExternalLink( $value );
if ( $this->mOptions->getExternalLinkTarget() ) {
@@ -5116,11 +5187,11 @@ class Parser {
$params['frame']['title'] = $this->stripAltText( $caption, $holders );
}
- wfRunHooks( 'ParserMakeImageParams', array( $title, $file, &$params ) );
+ wfRunHooks( 'ParserMakeImageParams', array( $title, $file, &$params, $this ) );
# Linker does the rest
$time = isset( $options['time'] ) ? $options['time'] : false;
- $ret = Linker::makeImageLink2( $title, $file, $params['frame'], $params['handler'],
+ $ret = Linker::makeImageLink( $this, $title, $file, $params['frame'], $params['handler'],
$time, $descQuery, $this->mOptions->getThumbSize() );
# Give the handler a chance to modify the parser object
@@ -5229,13 +5300,13 @@ class Parser {
*
* @param $text String: Page wikitext
* @param $section String: a section identifier string of the form:
- * <flag1> - <flag2> - ... - <section number>
+ * "<flag1> - <flag2> - ... - <section number>"
*
* Currently the only recognised flag is "T", which means the target section number
* was derived during a template inclusion parse, in other words this is a template
* section edit link. If no flags are given, it was an ordinary section edit link.
* This flag is required to avoid a section numbering mismatch when a section is
- * enclosed by <includeonly> (bug 6563).
+ * enclosed by "<includeonly>" (bug 6563).
*
* The section number 0 pulls the text before the first heading; other numbers will
* pull the given section along with its lower-level subsections. If the section is
@@ -5381,7 +5452,7 @@ class Parser {
* section does not exist, $oldtext is returned unchanged.
*
* @param $oldtext String: former text of the article
- * @param $section Numeric: section identifier
+ * @param $section int section identifier
* @param $text String: replacing text
* @return String: modified text
*/
@@ -5464,7 +5535,7 @@ class Parser {
/**
* Mutator for $mDefaultSort
*
- * @param $sort New value
+ * @param $sort string New value
*/
public function setDefaultSort( $sort ) {
$this->mDefaultSort = $sort;
@@ -5542,7 +5613,7 @@ class Parser {
*
* @param $text String: text string to be stripped of wikitext
* for use in a Section anchor
- * @return Filtered text string
+ * @return string Filtered text string
*/
public function stripSectionName( $text ) {
# Strip internal link markup
@@ -5553,7 +5624,7 @@ class Parser {
# @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 );
+ $text = preg_replace( '/\[(?i:' . $this->mUrlProtocols . ')([^ ]+?) ([^[]+)\]/', '$2', $text );
# Parse wikitext quotes (italics & bold)
$text = $this->doQuotes( $text );
@@ -5691,7 +5762,7 @@ class Parser {
* If the $data array has been stored persistently, the caller should first
* check whether it is still valid, by calling isValidHalfParsedText().
*
- * @param $data Serialized data
+ * @param $data array Serialized data
* @return String
*/
function unserializeHalfParsedText( $data ) {
@@ -5722,4 +5793,32 @@ class Parser {
function isValidHalfParsedText( $data ) {
return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;
}
+
+ /**
+ * Parsed a width param of imagelink like 300px or 200x300px
+ *
+ * @param $value String
+ *
+ * @return array
+ * @since 1.20
+ */
+ public function parseWidthParam( $value ) {
+ $parsedWidthParam = array();
+ if( $value === '' ) {
+ return $parsedWidthParam;
+ }
+ $m = array();
+ # (bug 13500) In both cases (width/height and width only),
+ # permit trailing "px" for backward compatibility.
+ if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
+ $width = intval( $m[1] );
+ $height = intval( $m[2] );
+ $parsedWidthParam['width'] = $width;
+ $parsedWidthParam['height'] = $height;
+ } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
+ $width = intval( $value );
+ $parsedWidthParam['width'] = $width;
+ }
+ return $parsedWidthParam;
+ }
}
diff --git a/includes/parser/ParserCache.php b/includes/parser/ParserCache.php
index 8b043290..6a4ef0c5 100644
--- a/includes/parser/ParserCache.php
+++ b/includes/parser/ParserCache.php
@@ -2,7 +2,23 @@
/**
* Cache for outputs of the PHP parser
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Cache Parser
*/
/**
@@ -77,6 +93,7 @@ class ParserCache {
*
* @param $article Article
* @param $popts ParserOptions
+ * @return string
*/
function getETag( $article, $popts ) {
return 'W/"' . $this->getParserOutputKey( $article,
@@ -88,7 +105,7 @@ class ParserCache {
* Retrieve the ParserOutput from ParserCache, even if it's outdated.
* @param $article Article
* @param $popts ParserOptions
- * @return ParserOutput|false
+ * @return ParserOutput|bool False on failure
*/
public function getDirty( $article, $popts ) {
$value = $this->get( $article, $popts, true );
@@ -102,8 +119,10 @@ class ParserCache {
*
* @todo Document parameter $useOutdated
*
- * @param $article Article
- * @param $popts ParserOptions
+ * @param $article Article
+ * @param $popts ParserOptions
+ * @param $useOutdated Boolean (default true)
+ * @return bool|mixed|string
*/
public function getKey( $article, $popts, $useOutdated = true ) {
global $wgCacheEpoch;
@@ -139,11 +158,11 @@ class ParserCache {
* Retrieve the ParserOutput from ParserCache.
* false if not found or outdated.
*
- * @param $article Article
- * @param $popts ParserOptions
- * @param $useOutdated
+ * @param $article Article
+ * @param $popts ParserOptions
+ * @param $useOutdated Boolean (default false)
*
- * @return ParserOutput|false
+ * @return ParserOutput|bool False on failure
*/
public function get( $article, $popts, $useOutdated = false ) {
global $wgCacheEpoch;
diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php
index 57d3a7eb..009b18a1 100644
--- a/includes/parser/ParserOptions.php
+++ b/includes/parser/ParserOptions.php
@@ -1,6 +1,21 @@
<?php
/**
- * \brief Options for the PHP parser
+ * Options for the PHP parser
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Parser
@@ -79,6 +94,11 @@ class ParserOptions {
* Maximum number of nodes touched by PPFrame::expand()
*/
var $mMaxPPNodeCount;
+
+ /**
+ * Maximum number of nodes generated by Preprocessor::preprocessToObj()
+ */
+ var $mMaxGeneratedPPNodeCount;
/**
* Maximum recursion depth in PPFrame::expand()
@@ -91,6 +111,11 @@ class ParserOptions {
var $mMaxTemplateDepth;
/**
+ * Maximum number of calls per parse to expensive parser functions
+ */
+ var $mExpensiveParserFunctionLimit;
+
+ /**
* Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
*/
var $mRemoveComments = true;
@@ -130,6 +155,16 @@ class ParserOptions {
var $mPreSaveTransform = true;
/**
+ * Whether content conversion should be disabled
+ */
+ var $mDisableContentConversion;
+
+ /**
+ * Whether title conversion should be disabled
+ */
+ var $mDisableTitleConversion;
+
+ /**
* Automatically number headings?
*/
var $mNumberHeadings;
@@ -199,13 +234,18 @@ class ParserOptions {
function getTargetLanguage() { return $this->mTargetLanguage; }
function getMaxIncludeSize() { return $this->mMaxIncludeSize; }
function getMaxPPNodeCount() { return $this->mMaxPPNodeCount; }
+ function getMaxGeneratedPPNodeCount() { return $this->mMaxGeneratedPPNodeCount; }
function getMaxPPExpandDepth() { return $this->mMaxPPExpandDepth; }
function getMaxTemplateDepth() { return $this->mMaxTemplateDepth; }
+ /* @since 1.20 */
+ function getExpensiveParserFunctionLimit() { return $this->mExpensiveParserFunctionLimit; }
function getRemoveComments() { return $this->mRemoveComments; }
function getTemplateCallback() { return $this->mTemplateCallback; }
function getEnableLimitReport() { return $this->mEnableLimitReport; }
function getCleanSignatures() { return $this->mCleanSignatures; }
function getExternalLinkTarget() { return $this->mExternalLinkTarget; }
+ function getDisableContentConversion() { return $this->mDisableContentConversion; }
+ function getDisableTitleConversion() { return $this->mDisableTitleConversion; }
function getMath() { $this->optionUsed( 'math' );
return $this->mMath; }
function getThumbSize() { $this->optionUsed( 'thumbsize' );
@@ -285,13 +325,18 @@ class ParserOptions {
function setTargetLanguage( $x ) { return wfSetVar( $this->mTargetLanguage, $x, true ); }
function setMaxIncludeSize( $x ) { return wfSetVar( $this->mMaxIncludeSize, $x ); }
function setMaxPPNodeCount( $x ) { return wfSetVar( $this->mMaxPPNodeCount, $x ); }
+ function setMaxGeneratedPPNodeCount( $x ) { return wfSetVar( $this->mMaxGeneratedPPNodeCount, $x ); }
function setMaxTemplateDepth( $x ) { return wfSetVar( $this->mMaxTemplateDepth, $x ); }
+ /* @since 1.20 */
+ function setExpensiveParserFunctionLimit( $x ) { return wfSetVar( $this->mExpensiveParserFunctionLimit, $x ); }
function setRemoveComments( $x ) { return wfSetVar( $this->mRemoveComments, $x ); }
function setTemplateCallback( $x ) { return wfSetVar( $this->mTemplateCallback, $x ); }
function enableLimitReport( $x = true ) { return wfSetVar( $this->mEnableLimitReport, $x ); }
function setTimestamp( $x ) { return wfSetVar( $this->mTimestamp, $x ); }
function setCleanSignatures( $x ) { return wfSetVar( $this->mCleanSignatures, $x ); }
function setExternalLinkTarget( $x ) { return wfSetVar( $this->mExternalLinkTarget, $x ); }
+ function disableContentConversion( $x = true ) { return wfSetVar( $this->mDisableContentConversion, $x ); }
+ function disableTitleConversion( $x = true ) { return wfSetVar( $this->mDisableTitleConversion, $x ); }
function setMath( $x ) { return wfSetVar( $this->mMath, $x ); }
function setUserLang( $x ) {
if ( is_string( $x ) ) {
@@ -380,7 +425,8 @@ class ParserOptions {
global $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages,
$wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
$wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
- $wgCleanSignatures, $wgExternalLinkTarget;
+ $wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
+ $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion;
wfProfileIn( __METHOD__ );
@@ -392,10 +438,14 @@ class ParserOptions {
$this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
$this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
$this->mMaxPPNodeCount = $wgMaxPPNodeCount;
+ $this->mMaxGeneratedPPNodeCount = $wgMaxGeneratedPPNodeCount;
$this->mMaxPPExpandDepth = $wgMaxPPExpandDepth;
$this->mMaxTemplateDepth = $wgMaxTemplateDepth;
+ $this->mExpensiveParserFunctionLimit = $wgExpensiveParserFunctionLimit;
$this->mCleanSignatures = $wgCleanSignatures;
$this->mExternalLinkTarget = $wgExternalLinkTarget;
+ $this->mDisableContentConversion = $wgDisableLangConversion;
+ $this->mDisableTitleConversion = $wgDisableLangConversion || $wgDisableTitleConversion;
$this->mUser = $user;
$this->mNumberHeadings = $user->getOption( 'numberheadings' );
@@ -428,6 +478,7 @@ class ParserOptions {
* Returns the full array of options that would have been used by
* in 1.16.
* Used to get the old parser cache entries when available.
+ * @return array
*/
public static function legacyOptions() {
global $wgUseDynamicDates;
diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php
index 2d99a3b5..41b4a385 100644
--- a/includes/parser/ParserOutput.php
+++ b/includes/parser/ParserOutput.php
@@ -1,118 +1,26 @@
<?php
+
/**
- * Output of the PHP parser
+ * Output of the PHP parser.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Parser
*/
-
-/**
- * @todo document
- * @ingroup Parser
- */
-
-class CacheTime {
- var $mVersion = Parser::VERSION, # Compatibility check
- $mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
- $mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncachable. Used in ParserCache.
- $mContainsOldMagic; # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
-
- function getCacheTime() { return $this->mCacheTime; }
-
- function containsOldMagic() { return $this->mContainsOldMagic; }
- function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
-
- /**
- * setCacheTime() sets the timestamp expressing when the page has been rendered.
- * This doesn not control expiry, see updateCacheExpiry() for that!
- * @param $t string
- * @return string
- */
- function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
-
- /**
- * Sets the number of seconds after which this object should expire.
- * This value is used with the ParserCache.
- * If called with a value greater than the value provided at any previous call,
- * the new call has no effect. The value returned by getCacheExpiry is smaller
- * or equal to the smallest number that was provided as an argument to
- * updateCacheExpiry().
- *
- * @param $seconds number
- */
- function updateCacheExpiry( $seconds ) {
- $seconds = (int)$seconds;
-
- if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
- $this->mCacheExpiry = $seconds;
- }
-
- // hack: set old-style marker for uncacheable entries.
- if ( $this->mCacheExpiry !== null && $this->mCacheExpiry <= 0 ) {
- $this->mCacheTime = -1;
- }
- }
-
- /**
- * Returns the number of seconds after which this object should expire.
- * This method is used by ParserCache to determine how long the ParserOutput can be cached.
- * The timestamp of expiry can be calculated by adding getCacheExpiry() to getCacheTime().
- * The value returned by getCacheExpiry is smaller or equal to the smallest number
- * that was provided to a call of updateCacheExpiry(), and smaller or equal to the
- * value of $wgParserCacheExpireTime.
- */
- function getCacheExpiry() {
- global $wgParserCacheExpireTime;
-
- if ( $this->mCacheTime < 0 ) {
- return 0;
- } // old-style marker for "not cachable"
-
- $expire = $this->mCacheExpiry;
-
- if ( $expire === null ) {
- $expire = $wgParserCacheExpireTime;
- } else {
- $expire = min( $expire, $wgParserCacheExpireTime );
- }
-
- if( $this->containsOldMagic() ) { //compatibility hack
- $expire = min( $expire, 3600 ); # 1 hour
- }
-
- if ( $expire <= 0 ) {
- return 0; // not cachable
- } else {
- return $expire;
- }
- }
-
- /**
- * @return bool
- */
- function isCacheable() {
- return $this->getCacheExpiry() > 0;
- }
-
- /**
- * Return true if this cached output object predates the global or
- * per-article cache invalidation timestamps, or if it comes from
- * an incompatible older version.
- *
- * @param $touched String: the affected article's last touched timestamp
- * @return Boolean
- */
- public function expired( $touched ) {
- global $wgCacheEpoch;
- return !$this->isCacheable() || // parser says it's uncacheable
- $this->getCacheTime() < $touched ||
- $this->getCacheTime() <= $wgCacheEpoch ||
- $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
- !isset( $this->mVersion ) ||
- version_compare( $this->mVersion, Parser::VERSION, "lt" );
- }
-}
-
class ParserOutput extends CacheTime {
var $mText, # The output text
$mLanguageLinks, # List of the full text of language links, in the order they appear
@@ -140,8 +48,9 @@ class ParserOutput extends CacheTime {
$mProperties = array(), # Name/value pairs to be cached in the DB
$mTOCHTML = '', # HTML of the TOC
$mTimestamp; # Timestamp of the revision
- private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
- private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
+ private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
+ private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
+ private $mSecondaryDataUpdates = array(); # List of instances of SecondaryDataObject(), used to cause some information extracted from the page in a custom place.
const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
@@ -166,6 +75,7 @@ class ParserOutput extends CacheTime {
/**
* callback used by getText to replace editsection tokens
* @private
+ * @return mixed
*/
function replaceEditSectionLinksCallback( $m ) {
global $wgOut, $wgLang;
@@ -331,7 +241,7 @@ class ParserOutput extends CacheTime {
}
/**
- * Add some text to the <head>.
+ * Add some text to the "<head>".
* If $tag is set, the section with that tag will only be included once
* in a given page.
*/
@@ -447,4 +357,45 @@ class ParserOutput extends CacheTime {
function recordOption( $option ) {
$this->mAccessedOptions[$option] = true;
}
+
+ /**
+ * Adds an update job to the output. Any update jobs added to the output will eventually bexecuted in order to
+ * store any secondary information extracted from the page's content.
+ *
+ * @since 1.20
+ *
+ * @param DataUpdate $update
+ */
+ public function addSecondaryDataUpdate( DataUpdate $update ) {
+ $this->mSecondaryDataUpdates[] = $update;
+ }
+
+ /**
+ * Returns any DataUpdate jobs to be executed in order to store secondary information
+ * extracted from the page's content, including a LinksUpdate object for all links stored in
+ * this ParserOutput object.
+ *
+ * @since 1.20
+ *
+ * @param $title Title of the page we're updating. If not given, a title object will be created based on $this->getTitleText()
+ * @param $recursive Boolean: queue jobs for recursive updates?
+ *
+ * @return Array. An array of instances of DataUpdate
+ */
+ public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
+ if ( is_null( $title ) ) {
+ $title = Title::newFromText( $this->getTitleText() );
+ }
+
+ $linksUpdate = new LinksUpdate( $title, $this, $recursive );
+
+ if ( $this->mSecondaryDataUpdates === array() ) {
+ return array( $linksUpdate );
+ } else {
+ $updates = array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
+ }
+
+ return $updates;
+ }
+
}
diff --git a/includes/parser/Parser_DiffTest.php b/includes/parser/Parser_DiffTest.php
index efad33f9..f25340fa 100644
--- a/includes/parser/Parser_DiffTest.php
+++ b/includes/parser/Parser_DiffTest.php
@@ -2,7 +2,23 @@
/**
* Fake parser that output the difference of two different parsers
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
diff --git a/includes/parser/Parser_LinkHooks.php b/includes/parser/Parser_LinkHooks.php
index 90e44943..6bcc324d 100644
--- a/includes/parser/Parser_LinkHooks.php
+++ b/includes/parser/Parser_LinkHooks.php
@@ -2,7 +2,23 @@
/**
* Modified version of the PHP parser with hooks for wiki links; experimental
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -84,7 +100,7 @@ class Parser_LinkHooks extends Parser {
* @param $flags Integer: a combination of the following flags:
* SLH_PATTERN Use a regex link pattern rather than a namespace
*
- * @return The old callback function for this name, if any
+ * @return callback|null The old callback function for this name, if any
*/
public function setLinkHook( $ns, $callback, $flags = 0 ) {
if( $flags & SLH_PATTERN && !is_string($ns) )
@@ -210,7 +226,7 @@ class Parser_LinkHooks extends Parser {
# 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() . ')/', $titleText) ) {
+ if( preg_match('/^\b(?i:' . wfUrlProtocols() . ')/', $titleText) ) {
wfProfileOut( __METHOD__ );
return $wt;
}
diff --git a/includes/parser/Preprocessor.php b/includes/parser/Preprocessor.php
index ae088fdb..bd13f9ae 100644
--- a/includes/parser/Preprocessor.php
+++ b/includes/parser/Preprocessor.php
@@ -2,7 +2,23 @@
/**
* Interfaces for preprocessors
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -62,15 +78,19 @@ interface PPFrame {
const RECOVER_ORIG = 27; // = 1|2|8|16 no constant expression support in PHP yet
+ /** This constant exists when $indexOffset is supported in newChild() */
+ const SUPPORTS_INDEX_OFFSET = 1;
+
/**
* Create a child frame
*
* @param $args array
* @param $title Title
+ * @param $indexOffset A number subtracted from the index attributes of the arguments
*
* @return PPFrame
*/
- function newChild( $args = false, $title = false );
+ function newChild( $args = false, $title = false, $indexOffset = 0 );
/**
* Expand a document tree node
@@ -211,7 +231,7 @@ interface PPNode {
function getName();
/**
- * Split a <part> node into an associative array containing:
+ * Split a "<part>" node into an associative array containing:
* name PPNode name
* index String index
* value PPNode value
@@ -219,13 +239,13 @@ interface PPNode {
function splitArg();
/**
- * Split an <ext> node into an associative array containing name, attr, inner and close
+ * Split an "<ext>" node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
*/
function splitExt();
/**
- * Split an <h> node
+ * Split an "<h>" node
*/
function splitHeading();
}
diff --git a/includes/parser/Preprocessor_DOM.php b/includes/parser/Preprocessor_DOM.php
index 066589f6..34de0ba5 100644
--- a/includes/parser/Preprocessor_DOM.php
+++ b/includes/parser/Preprocessor_DOM.php
@@ -2,6 +2,21 @@
/**
* Preprocessor using PHP's dom extension
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Parser
*/
@@ -41,7 +56,7 @@ class Preprocessor_DOM implements Preprocessor {
}
/**
- * @param $args
+ * @param $args array
* @return PPCustomFrame_DOM
*/
function newCustomFrame( $args ) {
@@ -97,7 +112,7 @@ class Preprocessor_DOM implements Preprocessor {
*
* @param $text String: the text to parse
* @param $flags Integer: bitwise combination of:
- * Parser::PTD_FOR_INCLUSION Handle <noinclude>/<includeonly> as if the text is being
+ * Parser::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>" as if the text is being
* included. Default is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
@@ -147,6 +162,15 @@ class Preprocessor_DOM implements Preprocessor {
}
}
+
+ // Fail if the number of elements exceeds acceptable limits
+ // Do not attempt to generate the DOM
+ $this->parser->mGeneratedPPNodeCount += substr_count( $xml, '<' );
+ $max = $this->parser->mOptions->getMaxGeneratedPPNodeCount();
+ if ( $this->parser->mGeneratedPPNodeCount > $max ) {
+ throw new MWException( __METHOD__.': generated node count limit exceeded' );
+ }
+
wfProfileIn( __METHOD__.'-loadXML' );
$dom = new DOMDocument;
wfSuppressWarnings();
@@ -220,6 +244,7 @@ class Preprocessor_DOM implements Preprocessor {
$searchBase = "[{<\n"; #}
$revText = strrev( $text ); // For fast reverse searches
+ $lengthText = strlen( $text );
$i = 0; # Input pointer, starts out pointing to a pseudo-newline before the start
$accum =& $stack->getAccum(); # Current accumulator
@@ -275,7 +300,7 @@ class Preprocessor_DOM implements Preprocessor {
$accum .= htmlspecialchars( substr( $text, $i, $literalLength ) );
$i += $literalLength;
}
- if ( $i >= strlen( $text ) ) {
+ if ( $i >= $lengthText ) {
if ( $currentClosing == "\n" ) {
// Do a past-the-end run to finish off the heading
$curChar = '';
@@ -339,10 +364,10 @@ class Preprocessor_DOM implements Preprocessor {
// Unclosed comment in input, runs to end
$inner = substr( $text, $i );
$accum .= '<comment>' . htmlspecialchars( $inner ) . '</comment>';
- $i = strlen( $text );
+ $i = $lengthText;
} else {
// Search backwards for leading whitespace
- $wsStart = $i ? ( $i - strspn( $revText, ' ', strlen( $text ) - $i ) ) : 0;
+ $wsStart = $i ? ( $i - strspn( $revText, ' ', $lengthText - $i ) ) : 0;
// Search forwards for trailing whitespace
// $wsEnd will be the position of the last space (or the '>' if there's none)
$wsEnd = $endPos + 2 + strspn( $text, ' ', $endPos + 3 );
@@ -423,7 +448,7 @@ class Preprocessor_DOM implements Preprocessor {
} else {
// No end tag -- let it run out to the end of the text.
$inner = substr( $text, $tagEndPos + 1 );
- $i = strlen( $text );
+ $i = $lengthText;
$close = '';
}
}
@@ -479,20 +504,20 @@ class Preprocessor_DOM implements Preprocessor {
} elseif ( $found == 'line-end' ) {
$piece = $stack->top;
// A heading must be open, otherwise \n wouldn't have been in the search list
- assert( $piece->open == "\n" );
+ assert( '$piece->open == "\n"' );
$part = $piece->getCurrentPart();
// Search back through the input to see if it has a proper close
// Do this using the reversed string since the other solutions (end anchor, etc.) are inefficient
- $wsLength = strspn( $revText, " \t", strlen( $text ) - $i );
+ $wsLength = strspn( $revText, " \t", $lengthText - $i );
$searchStart = $i - $wsLength;
if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
// Comment found at line end
// Search for equals signs before the comment
$searchStart = $part->visualEnd;
- $searchStart -= strspn( $revText, " \t", strlen( $text ) - $searchStart );
+ $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
}
$count = $piece->count;
- $equalsLength = strspn( $revText, '=', strlen( $text ) - $searchStart );
+ $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
if ( $equalsLength > 0 ) {
if ( $searchStart - $equalsLength == $piece->startPos ) {
// This is just a single string of equals signs on its own line
@@ -911,7 +936,7 @@ class PPFrame_DOM implements PPFrame {
*
* @return PPTemplateFrame_DOM
*/
- function newChild( $args = false, $title = false ) {
+ function newChild( $args = false, $title = false, $indexOffset = 0 ) {
$namedArgs = array();
$numberedArgs = array();
if ( $title === false ) {
@@ -923,6 +948,9 @@ class PPFrame_DOM implements PPFrame {
$args = $args->node;
}
foreach ( $args as $arg ) {
+ if ( $arg instanceof PPNode ) {
+ $arg = $arg->node;
+ }
if ( !$xpath ) {
$xpath = new DOMXPath( $arg->ownerDocument );
}
@@ -932,6 +960,7 @@ class PPFrame_DOM implements PPFrame {
if ( $nameNodes->item( 0 )->hasAttributes() ) {
// Numbered parameter
$index = $nameNodes->item( 0 )->attributes->getNamedItem( 'index' )->textContent;
+ $index = $index - $indexOffset;
$numberedArgs[$index] = $value->item( 0 );
unset( $namedArgs[$index] );
} else {
@@ -958,14 +987,25 @@ class PPFrame_DOM implements PPFrame {
}
if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
+ $this->parser->limitationWarn( 'node-count-exceeded',
+ $this->parser->mPPNodeCount,
+ $this->parser->mOptions->getMaxPPNodeCount()
+ );
return '<span class="error">Node-count limit exceeded</span>';
}
if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
+ $this->parser->limitationWarn( 'expansion-depth-exceeded',
+ $expansionDepth,
+ $this->parser->mOptions->getMaxPPExpandDepth()
+ );
return '<span class="error">Expansion depth limit exceeded</span>';
}
wfProfileIn( __METHOD__ );
++$expansionDepth;
+ if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
+ $this->parser->mHighestExpansionDepth = $expansionDepth;
+ }
if ( $root instanceof PPNode_DOM ) {
$root = $root->node;
@@ -1250,6 +1290,7 @@ class PPFrame_DOM implements PPFrame {
/**
* Virtual implode with brackets
+ * @return array
*/
function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
$args = array_slice( func_get_args(), 3 );
@@ -1522,6 +1563,10 @@ class PPCustomFrame_DOM extends PPFrame_DOM {
}
return $this->args[$index];
}
+
+ function getArguments() {
+ return $this->args;
+ }
}
/**
@@ -1623,10 +1668,10 @@ class PPNode_DOM implements PPNode {
}
/**
- * Split a <part> node into an associative array containing:
- * name PPNode name
- * index String index
- * value PPNode value
+ * Split a "<part>" node into an associative array containing:
+ * - name PPNode name
+ * - index String index
+ * - value PPNode value
*
* @return array
*/
@@ -1646,7 +1691,7 @@ class PPNode_DOM implements PPNode {
}
/**
- * Split an <ext> node into an associative array containing name, attr, inner and close
+ * Split an "<ext>" node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
*
* @return array
@@ -1673,7 +1718,8 @@ class PPNode_DOM implements PPNode {
}
/**
- * Split a <h> node
+ * Split a "<h>" node
+ * @return array
*/
function splitHeading() {
if ( $this->getName() !== 'h' ) {
diff --git a/includes/parser/Preprocessor_Hash.php b/includes/parser/Preprocessor_Hash.php
index 2934181a..4f04c865 100644
--- a/includes/parser/Preprocessor_Hash.php
+++ b/includes/parser/Preprocessor_Hash.php
@@ -2,6 +2,21 @@
/**
* Preprocessor using PHP arrays
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Parser
*/
@@ -9,7 +24,7 @@
/**
* Differences from DOM schema:
* * attribute nodes are children
- * * <h> nodes that aren't at the top are replaced with <possible-h>
+ * * "<h>" nodes that aren't at the top are replaced with <possible-h>
* @ingroup Parser
*/
class Preprocessor_Hash implements Preprocessor {
@@ -32,7 +47,7 @@ class Preprocessor_Hash implements Preprocessor {
}
/**
- * @param $args
+ * @param $args array
* @return PPCustomFrame_Hash
*/
function newCustomFrame( $args ) {
@@ -76,7 +91,7 @@ class Preprocessor_Hash implements Preprocessor {
*
* @param $text String: the text to parse
* @param $flags Integer: bitwise combination of:
- * Parser::PTD_FOR_INCLUSION Handle <noinclude>/<includeonly> as if the text is being
+ * Parser::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>" as if the text is being
* included. Default is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
@@ -162,6 +177,7 @@ class Preprocessor_Hash implements Preprocessor {
$searchBase = "[{<\n";
$revText = strrev( $text ); // For fast reverse searches
+ $lengthText = strlen( $text );
$i = 0; # Input pointer, starts out pointing to a pseudo-newline before the start
$accum =& $stack->getAccum(); # Current accumulator
@@ -216,7 +232,7 @@ class Preprocessor_Hash implements Preprocessor {
$accum->addLiteral( substr( $text, $i, $literalLength ) );
$i += $literalLength;
}
- if ( $i >= strlen( $text ) ) {
+ if ( $i >= $lengthText ) {
if ( $currentClosing == "\n" ) {
// Do a past-the-end run to finish off the heading
$curChar = '';
@@ -280,10 +296,10 @@ class Preprocessor_Hash implements Preprocessor {
// Unclosed comment in input, runs to end
$inner = substr( $text, $i );
$accum->addNodeWithText( 'comment', $inner );
- $i = strlen( $text );
+ $i = $lengthText;
} else {
// Search backwards for leading whitespace
- $wsStart = $i ? ( $i - strspn( $revText, ' ', strlen( $text ) - $i ) ) : 0;
+ $wsStart = $i ? ( $i - strspn( $revText, ' ', $lengthText - $i ) ) : 0;
// Search forwards for trailing whitespace
// $wsEnd will be the position of the last space (or the '>' if there's none)
$wsEnd = $endPos + 2 + strspn( $text, ' ', $endPos + 3 );
@@ -368,7 +384,7 @@ class Preprocessor_Hash implements Preprocessor {
} else {
// No end tag -- let it run out to the end of the text.
$inner = substr( $text, $tagEndPos + 1 );
- $i = strlen( $text );
+ $i = $lengthText;
$close = null;
}
}
@@ -428,20 +444,20 @@ class Preprocessor_Hash implements Preprocessor {
} elseif ( $found == 'line-end' ) {
$piece = $stack->top;
// A heading must be open, otherwise \n wouldn't have been in the search list
- assert( $piece->open == "\n" );
+ assert( '$piece->open == "\n"' );
$part = $piece->getCurrentPart();
// Search back through the input to see if it has a proper close
// Do this using the reversed string since the other solutions (end anchor, etc.) are inefficient
- $wsLength = strspn( $revText, " \t", strlen( $text ) - $i );
+ $wsLength = strspn( $revText, " \t", $lengthText - $i );
$searchStart = $i - $wsLength;
if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
// Comment found at line end
// Search for equals signs before the comment
$searchStart = $part->visualEnd;
- $searchStart -= strspn( $revText, " \t", strlen( $text ) - $searchStart );
+ $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
}
$count = $piece->count;
- $equalsLength = strspn( $revText, '=', strlen( $text ) - $searchStart );
+ $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
if ( $equalsLength > 0 ) {
if ( $searchStart - $equalsLength == $piece->startPos ) {
// This is just a single string of equals signs on its own line
@@ -869,11 +885,11 @@ class PPFrame_Hash implements PPFrame {
* $args is optionally a multi-root PPNode or array containing the template arguments
*
* @param $args PPNode_Hash_Array|array
- * @param $title Title|false
+ * @param $title Title|bool
*
* @return PPTemplateFrame_Hash
*/
- function newChild( $args = false, $title = false ) {
+ function newChild( $args = false, $title = false, $indexOffset = 0 ) {
$namedArgs = array();
$numberedArgs = array();
if ( $title === false ) {
@@ -889,8 +905,9 @@ class PPFrame_Hash implements PPFrame {
$bits = $arg->splitArg();
if ( $bits['index'] !== '' ) {
// Numbered parameter
- $numberedArgs[$bits['index']] = $bits['value'];
- unset( $namedArgs[$bits['index']] );
+ $index = $bits['index'] - $indexOffset;
+ $numberedArgs[$index] = $bits['value'];
+ unset( $namedArgs[$index] );
} else {
// Named parameter
$name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
@@ -915,12 +932,23 @@ class PPFrame_Hash implements PPFrame {
}
if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
+ $this->parser->limitationWarn( 'node-count-exceeded',
+ $this->parser->mPPNodeCount,
+ $this->parser->mOptions->getMaxPPNodeCount()
+ );
return '<span class="error">Node-count limit exceeded</span>';
}
if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
+ $this->parser->limitationWarn( 'expansion-depth-exceeded',
+ $expansionDepth,
+ $this->parser->mOptions->getMaxPPExpandDepth()
+ );
return '<span class="error">Expansion depth limit exceeded</span>';
}
++$expansionDepth;
+ if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
+ $this->parser->mHighestExpansionDepth = $expansionDepth;
+ }
$outStack = array( '', '' );
$iteratorStack = array( false, $root );
@@ -1470,6 +1498,10 @@ class PPCustomFrame_Hash extends PPFrame_Hash {
}
return $this->args[$index];
}
+
+ function getArguments() {
+ return $this->args;
+ }
}
/**
@@ -1543,7 +1575,7 @@ class PPNode_Hash_Tree implements PPNode {
$children = array();
for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
if ( isset( $child->name ) && $child->name === $name ) {
- $children[] = $name;
+ $children[] = $child;
}
}
return $children;
@@ -1572,10 +1604,10 @@ class PPNode_Hash_Tree implements PPNode {
}
/**
- * Split a <part> node into an associative array containing:
- * name PPNode name
- * index String index
- * value PPNode value
+ * Split a "<part>" node into an associative array containing:
+ * - name PPNode name
+ * - index String index
+ * - value PPNode value
*
* @return array
*/
@@ -1607,7 +1639,7 @@ class PPNode_Hash_Tree implements PPNode {
}
/**
- * Split an <ext> node into an associative array containing name, attr, inner and close
+ * Split an "<ext>" node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
*
* @return array
@@ -1635,7 +1667,7 @@ class PPNode_Hash_Tree implements PPNode {
}
/**
- * Split an <h> node
+ * Split an "<h>" node
*
* @return array
*/
@@ -1661,7 +1693,7 @@ class PPNode_Hash_Tree implements PPNode {
}
/**
- * Split a <template> or <tplarg> node
+ * Split a "<template>" or "<tplarg>" node
*
* @return array
*/
diff --git a/includes/parser/Preprocessor_HipHop.hphp b/includes/parser/Preprocessor_HipHop.hphp
index f5af0154..8b71a1b5 100644
--- a/includes/parser/Preprocessor_HipHop.hphp
+++ b/includes/parser/Preprocessor_HipHop.hphp
@@ -3,6 +3,21 @@
* A preprocessor optimised for HipHop, using HipHop-specific syntax.
* vim: ft=php
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Parser
*/
@@ -18,6 +33,9 @@ class Preprocessor_HipHop implements Preprocessor {
const CACHE_VERSION = 1;
+ /**
+ * @param $parser Parser
+ */
function __construct( $parser ) {
$this->parser = $parser;
}
@@ -30,10 +48,10 @@ class Preprocessor_HipHop implements Preprocessor {
}
/**
- * @param $args
+ * @param $args array
* @return PPCustomFrame_HipHop
*/
- function newCustomFrame( array $args ) {
+ function newCustomFrame( $args ) {
return new PPCustomFrame_HipHop( $this, $args );
}
@@ -88,15 +106,18 @@ class Preprocessor_HipHop implements Preprocessor {
* cache may be implemented at a later date which takes further advantage of these strict
* dependency requirements.
*
+ * @throws MWException
* @return PPNode_HipHop_Tree
*/
- function preprocessToObj( string $text, int $flags = 0 ) {
+ function preprocessToObj( $text, $flags = 0 ) {
wfProfileIn( __METHOD__ );
// Check cache.
global $wgMemc, $wgPreprocessorCacheThreshold;
- $cacheable = ($wgPreprocessorCacheThreshold !== false && strlen( $text ) > $wgPreprocessorCacheThreshold);
+ $lengthText = strlen( $text );
+
+ $cacheable = ($wgPreprocessorCacheThreshold !== false && $lengthText > $wgPreprocessorCacheThreshold);
if ( $cacheable ) {
wfProfileIn( __METHOD__.'-cacheable' );
@@ -220,7 +241,7 @@ class Preprocessor_HipHop implements Preprocessor {
$accum->addLiteral( strval( substr( $text, $i, $literalLength ) ) );
$i += $literalLength;
}
- if ( $i >= strlen( $text ) ) {
+ if ( $i >= $lengthText ) {
if ( $currentClosing === "\n" ) {
// Do a past-the-end run to finish off the heading
$curChar = '';
@@ -286,12 +307,12 @@ class Preprocessor_HipHop implements Preprocessor {
// Unclosed comment in input, runs to end
$inner = strval( substr( $text, $i ) );
$accum->addNodeWithText( 'comment', $inner );
- $i = strlen( $text );
+ $i = $lengthText;
} else {
$endPos = intval( $variantEndPos );
// Search backwards for leading whitespace
if ( $i ) {
- $wsStart = $i - intval( strspn( $revText, ' ', strlen( $text ) - $i ) );
+ $wsStart = $i - intval( strspn( $revText, ' ', $lengthText - $i ) );
} else {
$wsStart = 0;
}
@@ -384,7 +405,7 @@ class Preprocessor_HipHop implements Preprocessor {
} else {
// No end tag -- let it run out to the end of the text.
$inner = strval( substr( $text, $tagEndPos + 1 ) );
- $i = strlen( $text );
+ $i = $lengthText;
$haveClose = false;
}
}
@@ -444,20 +465,21 @@ class Preprocessor_HipHop implements Preprocessor {
} elseif ( $found === 'line-end' ) {
$piece = $stack->getTop();
// A heading must be open, otherwise \n wouldn't have been in the search list
- assert( $piece->open === "\n" );
+ assert( $piece->open === "\n" ); // Passing the assert condition directly instead of string, as
+ // HPHP /compiler/ chokes on strings when ASSERT_ACTIVE != 0.
$part = $piece->getCurrentPart();
// Search back through the input to see if it has a proper close
// Do this using the reversed string since the other solutions (end anchor, etc.) are inefficient
- $wsLength = intval( strspn( $revText, " \t", strlen( $text ) - $i ) );
+ $wsLength = intval( strspn( $revText, " \t", $lengthText - $i ) );
$searchStart = $i - $wsLength;
if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
// Comment found at line end
// Search for equals signs before the comment
$searchStart = intval( $part->visualEnd );
- $searchStart -= intval( strspn( $revText, " \t", strlen( $text ) - $searchStart ) );
+ $searchStart -= intval( strspn( $revText, " \t", $lengthText - $searchStart ) );
}
$count = intval( $piece->count );
- $equalsLength = intval( strspn( $revText, '=', strlen( $text ) - $searchStart ) );
+ $equalsLength = intval( strspn( $revText, '=', $lengthText - $searchStart ) );
$isTreeNode = false;
$resultAccum = $accum;
if ( $equalsLength > 0 ) {
@@ -814,16 +836,23 @@ class PPDStack_HipHop {
* @ingroup Parser
*/
class PPDStackElement_HipHop {
- var $open, // Opening character (\n for heading)
- $close, // Matching closing character
+ var $open, // Opening character (\n for heading)
+ $close, // Matching closing character
$count, // Number of opening characters found (number of "=" for heading)
$parts, // Array of PPDPart objects describing pipe-separated parts.
$lineStart; // True if the open char appeared at the start of the input line. Not set for headings.
+ /**
+ * @param $obj PPDStackElement_HipHop
+ * @return PPDStackElement_HipHop
+ */
static function cast( PPDStackElement_HipHop $obj ) {
return $obj;
}
+ /**
+ * @param $data array
+ */
function __construct( $data = array() ) {
$this->parts = array( new PPDPart_HipHop );
@@ -832,14 +861,23 @@ class PPDStackElement_HipHop {
}
}
+ /**
+ * @return PPDAccum_HipHop
+ */
function getAccum() {
return PPDAccum_HipHop::cast( $this->parts[count($this->parts) - 1]->out );
}
+ /**
+ * @param $s string
+ */
function addPart( $s = '' ) {
$this->parts[] = new PPDPart_HipHop( $s );
}
+ /**
+ * @return PPDPart_HipHop
+ */
function getCurrentPart() {
return PPDPart_HipHop::cast( $this->parts[count($this->parts) - 1] );
}
@@ -860,6 +898,7 @@ class PPDStackElement_HipHop {
/**
* Get the accumulator that would result if the close is not found.
*
+ * @param $openingCount bool
* @return PPDAccum_HipHop
*/
function breakSyntax( $openingCount = false ) {
@@ -1025,12 +1064,14 @@ class PPFrame_HipHop implements PPFrame {
* Create a new child frame
* $args is optionally a multi-root PPNode or array containing the template arguments
*
- * @param $args PPNode_HipHop_Array|array
- * @param $title Title|false
+ * @param $args PPNode_HipHop_Array|array|bool
+ * @param $title Title|bool
+ * @param $indexOffset A number subtracted from the index attributes of the arguments
*
+ * @throws MWException
* @return PPTemplateFrame_HipHop
*/
- function newChild( $args = false, $title = false ) {
+ function newChild( $args = false, $title = false, $indexOffset = 0 ) {
$namedArgs = array();
$numberedArgs = array();
if ( $title === false ) {
@@ -1072,12 +1113,23 @@ class PPFrame_HipHop implements PPFrame {
}
if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
+ $this->parser->limitationWarn( 'node-count-exceeded',
+ $this->parser->mPPNodeCount,
+ $this->parser->mOptions->getMaxPPNodeCount()
+ );
return '<span class="error">Node-count limit exceeded</span>';
}
if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
+ $this->parser->limitationWarn( 'expansion-depth-exceeded',
+ $expansionDepth,
+ $this->parser->mOptions->getMaxPPExpandDepth()
+ );
return '<span class="error">Expansion depth limit exceeded</span>';
}
++$expansionDepth;
+ if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
+ $this->parser->mHighestExpansionDepth = $expansionDepth;
+ }
$outStack = array( '', '' );
$iteratorStack = array( false, $root );
@@ -1266,6 +1318,7 @@ class PPFrame_HipHop implements PPFrame {
/**
* Implode with no flags specified
* This previously called implodeWithFlags but has now been inlined to reduce stack depth
+ * @param $sep
* @return string
*/
function implode( $sep /*, ... */ ) {
@@ -1296,6 +1349,7 @@ class PPFrame_HipHop implements PPFrame {
* Makes an object that, when expand()ed, will be the same as one obtained
* with implode()
*
+ * @param $sep
* @return PPNode_HipHop_Array
*/
function virtualImplode( $sep /*, ... */ ) {
@@ -1325,6 +1379,9 @@ class PPFrame_HipHop implements PPFrame {
/**
* Virtual implode with brackets
*
+ * @param $start
+ * @param $sep
+ * @param $end
* @return PPNode_HipHop_Array
*/
function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
@@ -1445,11 +1502,11 @@ class PPTemplateFrame_HipHop extends PPFrame_HipHop {
var $numberedExpansionCache, $namedExpansionCache;
/**
- * @param $preprocessor
- * @param $parent
+ * @param $preprocessor Preprocessor_HipHop
+ * @param $parent bool
* @param $numberedArgs array
* @param $namedArgs array
- * @param $title Title
+ * @param $title Title|bool
*/
function __construct( $preprocessor, $parent = false, $numberedArgs = array(), $namedArgs = array(), $title = false ) {
parent::__construct( $preprocessor );
@@ -1696,11 +1753,15 @@ class PPNode_HipHop_Tree implements PPNode {
return $this->nextSibling;
}
+ /**
+ * @param $name string
+ * @return array
+ */
function getChildrenOfType( $name ) {
$children = array();
for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
if ( isset( $child->name ) && $child->name === $name ) {
- $children[] = $name;
+ $children[] = $child;
}
}
return $children;
@@ -1734,6 +1795,7 @@ class PPNode_HipHop_Tree implements PPNode {
* index String index
* value PPNode value
*
+ * @throws MWException
* @return array
*/
function splitArg() {
@@ -1767,6 +1829,7 @@ class PPNode_HipHop_Tree implements PPNode {
* Split an <ext> node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
*
+ * @throws MWException
* @return array
*/
function splitExt() {
@@ -1794,6 +1857,7 @@ class PPNode_HipHop_Tree implements PPNode {
/**
* Split an <h> node
*
+ * @throws MWException
* @return array
*/
function splitHeading() {
diff --git a/includes/parser/StripState.php b/includes/parser/StripState.php
index 7ad80fa1..ad95d5f7 100644
--- a/includes/parser/StripState.php
+++ b/includes/parser/StripState.php
@@ -1,4 +1,25 @@
<?php
+/**
+ * Holder for stripped items when parsing wiki markup.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Parser
+ */
/**
* @todo document, briefly.
@@ -10,6 +31,10 @@ class StripState {
protected $regex;
protected $tempType, $tempMergePrefix;
+ protected $circularRefGuard;
+ protected $recursionLevel = 0;
+
+ const UNSTRIP_RECURSION_LIMIT = 20;
/**
* @param $prefix string
@@ -21,6 +46,7 @@ class StripState {
'general' => array()
);
$this->regex = "/{$this->prefix}([^\x7f]+)" . Parser::MARKER_SUFFIX . '/';
+ $this->circularRefGuard = array();
}
/**
@@ -92,12 +118,10 @@ class StripState {
}
wfProfileIn( __METHOD__ );
+ $oldType = $this->tempType;
$this->tempType = $type;
- do {
- $oldText = $text;
- $text = preg_replace_callback( $this->regex, array( $this, 'unstripCallback' ), $text );
- } while ( $text !== $oldText );
- $this->tempType = null;
+ $text = preg_replace_callback( $this->regex, array( $this, 'unstripCallback' ), $text );
+ $this->tempType = $oldType;
wfProfileOut( __METHOD__ );
return $text;
}
@@ -107,8 +131,25 @@ class StripState {
* @return array
*/
protected function unstripCallback( $m ) {
- if ( isset( $this->data[$this->tempType][$m[1]] ) ) {
- return $this->data[$this->tempType][$m[1]];
+ $marker = $m[1];
+ if ( isset( $this->data[$this->tempType][$marker] ) ) {
+ if ( isset( $this->circularRefGuard[$marker] ) ) {
+ return '<span class="error">'
+ . wfMessage( 'parser-unstrip-loop-warning' )->inContentLanguage()->text()
+ . '</span>';
+ }
+ if ( $this->recursionLevel >= self::UNSTRIP_RECURSION_LIMIT ) {
+ return '<span class="error">' .
+ wfMessage( 'parser-unstrip-recursion-limit' )
+ ->numParams( self::UNSTRIP_RECURSION_LIMIT )->inContentLanguage()->text() .
+ '</span>';
+ }
+ $this->circularRefGuard[$marker] = true;
+ $this->recursionLevel++;
+ $ret = $this->unstripType( $this->tempType, $this->data[$this->tempType][$marker] );
+ $this->recursionLevel--;
+ unset( $this->circularRefGuard[$marker] );
+ return $ret;
} else {
return $m[0];
}
diff --git a/includes/parser/Tidy.php b/includes/parser/Tidy.php
index 2b98f01d..ed2d436d 100644
--- a/includes/parser/Tidy.php
+++ b/includes/parser/Tidy.php
@@ -2,7 +2,23 @@
/**
* HTML validation and correction
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
+ * @ingroup Parser
*/
/**
@@ -14,6 +30,8 @@
*
* This re-uses some of the parser's UNIQ tricks, though some of it is private so it's
* duplicated. Perhaps we should create an abstract marker hiding class.
+ *
+ * @ingroup Parser
*/
class MWTidyWrapper {
@@ -143,7 +161,7 @@ class MWTidy {
*
* @param $text String: HTML to check
* @param $stderr Boolean: Whether to read result from STDERR rather than STDOUT
- * @param &$retval Exit code (-1 on internal error)
+ * @param &$retval int Exit code (-1 on internal error)
* @return mixed String or null
*/
private static function execExternalTidy( $text, $stderr = false, &$retval = null ) {
@@ -207,7 +225,7 @@ class MWTidy {
*
* @param $text String: HTML to check
* @param $stderr Boolean: Whether to read result from error status instead of output
- * @param &$retval Exit code (-1 on internal error)
+ * @param &$retval int Exit code (-1 on internal error)
* @return mixed String or null
*/
private static function execInternalTidy( $text, $stderr = false, &$retval = null ) {