X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FMagicWord.php;h=57cf4af7e1a9e0bf81b9e06254012a7018e00e5f;hb=e50f64ae296340369f68a2a39b35069128f143d3;hp=f7a9400db21620b4106e34f8bb18f52843f5325c;hpb=786e93fb3708f90e55811000eab2a6bbd6e3e08f;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/MagicWord.php b/includes/MagicWord.php index f7a9400db2..57cf4af7e1 100644 --- a/includes/MagicWord.php +++ b/includes/MagicWord.php @@ -1,7 +1,11 @@ 86400, + 'currentmonth1' => 86400, + 'currentmonthname' => 86400, + 'currentmonthnamegen' => 86400, + 'currentmonthabbrev' => 86400, + 'currentday' => 3600, + 'currentday2' => 3600, + 'currentdayname' => 3600, + 'currentyear' => 86400, + 'currenttime' => 3600, + 'currenthour' => 3600, + 'localmonth' => 86400, + 'localmonth1' => 86400, + 'localmonthname' => 86400, + 'localmonthnamegen' => 86400, + 'localmonthabbrev' => 86400, + 'localday' => 3600, + 'localday2' => 3600, + 'localdayname' => 3600, + 'localyear' => 86400, + 'localtime' => 3600, + 'localhour' => 3600, + 'numberofarticles' => 3600, + 'numberoffiles' => 3600, + 'numberofedits' => 3600, + 'currentweek' => 3600, + 'currentdow' => 3600, + 'localweek' => 3600, + 'localdow' => 3600, + 'numberofusers' => 3600, + 'numberofactiveusers' => 3600, + 'numberofpages' => 3600, + 'currentversion' => 86400, + 'currenttimestamp' => 3600, + 'localtimestamp' => 3600, + 'pagesinnamespace' => 3600, + 'numberofadmins' => 3600, + 'numberofviews' => 3600, + 'numberingroup' => 3600, + ); + + static public $mDoubleUnderscoreIDs = array( + 'notoc', + 'nogallery', + 'forcetoc', + 'toc', + 'noeditsection', + 'newsectionlink', + 'nonewsectionlink', + 'hiddencat', + 'index', + 'noindex', + 'staticredirect', + 'notitleconvert', + 'nocontentconvert', + ); + + static public $mSubstIDs = array( + 'subst', + 'safesubst', ); static public $mObjects = array(); + static public $mDoubleUnderscoreArray = null; /**#@-*/ - function __construct($id = 0, $syn = '', $cs = false) { + function __construct($id = 0, $syn = array(), $cs = false) { $this->mId = $id; $this->mSynonyms = (array)$syn; $this->mCaseSensitive = $cs; - $this->mRegex = ''; - $this->mRegexStart = ''; - $this->mVariableRegex = ''; - $this->mVariableStartToEndRegex = ''; - $this->mModified = false; } /** * Factory: creates an object representing an ID - * @static + * + * @param $id + * + * @return MagicWord */ static function &get( $id ) { - if (!array_key_exists( $id, self::$mObjects ) ) { + if ( !isset( self::$mObjects[$id] ) ) { $mw = new MagicWord(); $mw->load( $id ); self::$mObjects[$id] = $mw; @@ -132,6 +208,8 @@ class MagicWord { /** * Get an array of parser variable IDs + * + * @return array */ static function getVariableIDs() { if ( !self::$mVariableIDsInitialised ) { @@ -149,9 +227,56 @@ class MagicWord { return self::$mVariableIDs; } - # Initialises this object with an ID + /** + * Get an array of parser substitution modifier IDs + * @return array + */ + static function getSubstIDs() { + return self::$mSubstIDs; + } + + /** + * Allow external reads of TTL array + * + * @param $id int + * @return array + */ + static function getCacheTTL( $id ) { + if ( array_key_exists( $id, self::$mCacheTTLs ) ) { + return self::$mCacheTTLs[$id]; + } else { + return -1; + } + } + + /** + * Get a MagicWordArray of double-underscore entities + * + * @return MagicWordArray + */ + static function getDoubleUnderscoreArray() { + if ( is_null( self::$mDoubleUnderscoreArray ) ) { + self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs ); + } + return self::$mDoubleUnderscoreArray; + } + + /** + * Clear the self::$mObjects variable + * For use in parser tests + */ + public static function clearCache() { + self::$mObjects = array(); + } + + /** + * Initialises this object with an ID + * + * @param $id + */ function load( $id ) { global $wgContLang; + wfProfileIn( __METHOD__ ); $this->mId = $id; $wgContLang->getMagic( $this ); if ( !$this->mSynonyms ) { @@ -159,6 +284,7 @@ class MagicWord { #throw new MWException( "Error: invalid magic word '$id'" ); wfDebugLog( 'exception', "Error: invalid magic word '$id'\n" ); } + wfProfileOut( __METHOD__ ); } /** @@ -166,17 +292,17 @@ class MagicWord { * @private */ function initRegex() { - #$variableClass = Title::legalChars(); - # This was used for matching "$1" variables, but different uses of the feature will have - # different restrictions, which should be checked *after* the MagicWord has been matched, - # not here. - IMSoP - + // Sort the synonyms by length, descending, so that the longest synonym + // matches in precedence to the shortest + $synonyms = $this->mSynonyms; + usort( $synonyms, array( $this, 'compareStringLength' ) ); + $escSyn = array(); - foreach ( $this->mSynonyms as $synonym ) + foreach ( $synonyms as $synonym ) // In case a magic word contains /, like that's going to happen;) $escSyn[] = preg_quote( $synonym, '/' ); $this->mBaseRegex = implode( '|', $escSyn ); - + $case = $this->mCaseSensitive ? '' : 'iu'; $this->mRegex = "/{$this->mBaseRegex}/{$case}"; $this->mRegexStart = "/^(?:{$this->mBaseRegex})/{$case}"; @@ -185,8 +311,32 @@ class MagicWord { "/^(?:{$this->mBaseRegex})$/{$case}" ); } + /** + * A comparison function that returns -1, 0 or 1 depending on whether the + * first string is longer, the same length or shorter than the second + * string. + * + * @param $s1 string + * @param $s2 string + * + * @return int + */ + function compareStringLength( $s1, $s2 ) { + $l1 = strlen( $s1 ); + $l2 = strlen( $s2 ); + if ( $l1 < $l2 ) { + return 1; + } elseif ( $l1 > $l2 ) { + return -1; + } else { + return 0; + } + } + /** * Gets a regex representing matching the word + * + * @return string */ function getRegex() { if ($this->mRegex == '' ) { @@ -199,6 +349,8 @@ class MagicWord { * Gets the regexp case modifier to use, i.e. i or nothing, to be used if * one is using MagicWord::getBaseRegex(), otherwise it'll be included in * the complete expression + * + * @return string */ function getRegexCase() { if ( $this->mRegex === '' ) @@ -209,6 +361,8 @@ class MagicWord { /** * Gets a regex matching the word, if it is at the string start + * + * @return string */ function getRegexStart() { if ($this->mRegex == '' ) { @@ -219,6 +373,8 @@ class MagicWord { /** * regex without the slashes and what not + * + * @return string */ function getBaseRegex() { if ($this->mRegex == '') { @@ -229,18 +385,24 @@ class MagicWord { /** * Returns true if the text contains the word + * + * @param $text string + * * @return bool */ function match( $text ) { - return preg_match( $this->getRegex(), $text ); + return (bool)preg_match( $this->getRegex(), $text ); } /** * Returns true if the text starts with the word + * + * @param $text string + * * @return bool */ function matchStart( $text ) { - return preg_match( $this->getRegexStart(), $text ); + return (bool)preg_match( $this->getRegexStart(), $text ); } /** @@ -248,12 +410,16 @@ class MagicWord { * The return code is the matched string, if there's no variable * part in the regex and the matched variable part ($1) if there * is one. + * + * @param $text string + * + * @return string */ function matchVariableStartToEnd( $text ) { $matches = array(); $matchcount = preg_match( $this->getVariableStartToEndRegex(), $text, $matches ); if ( $matchcount == 0 ) { - return NULL; + return null; } else { # multiple matched parts (variable match); some will be empty because of # synonyms. The variable will be the second non-empty one so remove any @@ -262,8 +428,11 @@ class MagicWord { $matches = array_values(array_filter($matches)); - if ( count($matches) == 1 ) { return $matches[0]; } - else { return $matches[1]; } + if ( count($matches) == 1 ) { + return $matches[0]; + } else { + return $matches[1]; + } } } @@ -271,6 +440,10 @@ class MagicWord { /** * Returns true if the text matches the word, and alters the * input string, removing all instances of the word + * + * @param $text string + * + * @return bool */ function matchAndRemove( &$text ) { $this->mFound = false; @@ -278,6 +451,10 @@ class MagicWord { return $this->mFound; } + /** + * @param $text + * @return bool + */ function matchStartAndRemove( &$text ) { $this->mFound = false; $text = preg_replace_callback( $this->getRegexStart(), array( &$this, 'pregRemoveAndRecord' ), $text ); @@ -286,17 +463,24 @@ class MagicWord { /** * Used in matchAndRemove() - * @private - **/ - function pregRemoveAndRecord( ) { + * + * @return string + */ + function pregRemoveAndRecord() { $this->mFound = true; return ''; } /** * Replaces the word with something else + * + * @param $replacement + * @param $subject + * @param $limit int + * + * @return string */ - function replace( $replacement, $subject, $limit=-1 ) { + function replace( $replacement, $subject, $limit = -1 ) { $res = preg_replace( $this->getRegex(), StringUtils::escapeRegexReplacement( $replacement ), $subject, $limit ); $this->mModified = !($res === $subject); return $res; @@ -306,6 +490,11 @@ class MagicWord { * Variable handling: {{SUBST:xxx}} style words * Calls back a function to determine what to replace xxx with * Input word must contain $1 + * + * @param $text string + * @param $callback + * + * @return string */ function substituteCallback( $text, $callback ) { $res = preg_replace_callback( $this->getVariableRegex(), $callback, $text ); @@ -315,6 +504,8 @@ class MagicWord { /** * Matches the word, where $1 is a wildcard + * + * @return string */ function getVariableRegex() { if ( $this->mVariableRegex == '' ) { @@ -325,6 +516,8 @@ class MagicWord { /** * Matches the entire string, where $1 is a wildcard + * + * @return string */ function getVariableStartToEndRegex() { if ( $this->mVariableStartToEndRegex == '' ) { @@ -335,11 +528,18 @@ class MagicWord { /** * Accesses the synonym list directly + * + * @param $i int + * + * @return string */ function getSynonym( $i ) { return $this->mSynonyms[$i]; } + /** + * @return array + */ function getSynonyms() { return $this->mSynonyms; } @@ -347,6 +547,8 @@ class MagicWord { /** * Returns true if the last call to replace() or substituteCallback() * returned a modified text, otherwise false. + * + * @return bool */ function getWasModified(){ return $this->mModified; @@ -357,8 +559,14 @@ class MagicWord { * This method uses the php feature to do several replacements at the same time, * thereby gaining some efficiency. The result is placed in the out variable * $result. The return value is true if something was replaced. - * @static - **/ + * @todo Should this be static? It doesn't seem to be used at all + * + * @param $magicarr + * @param $subject + * @param $result + * + * @return bool + */ function replaceMultiple( $magicarr, $subject, &$result ){ $search = array(); $replace = array(); @@ -375,6 +583,9 @@ class MagicWord { /** * Adds all the synonyms of this MagicWord to an array, to allow quick * lookup in a list of magic words + * + * @param $array + * @param $value */ function addToArray( &$array, $value ) { global $wgContLang; @@ -383,10 +594,16 @@ class MagicWord { } } + /** + * @return bool + */ function isCaseSensitive() { return $this->mCaseSensitive; } + /** + * @return int + */ function getId() { return $this->mId; } @@ -394,11 +611,13 @@ class MagicWord { /** * Class for handling an array of magic words + * @ingroup Parser */ class MagicWordArray { var $names = array(); var $hash; var $baseRegex, $regex; + var $matches; function __construct( $names = array() ) { $this->names = $names; @@ -406,15 +625,18 @@ class MagicWordArray { /** * Add a magic word by name + * + * @param $name string */ public function add( $name ) { - global $wgContLang; $this->names[] = $name; $this->hash = $this->baseRegex = $this->regex = null; } /** * Add a number of magic words by name + * + * @param $names array */ public function addArray( $names ) { $this->names = array_merge( $this->names, array_values( $names ) ); @@ -465,7 +687,7 @@ class MagicWordArray { } /** - * Get an unanchored regex + * Get an unanchored regex that does not match parameters */ function getRegex() { if ( is_null( $this->regex ) ) { @@ -482,14 +704,35 @@ class MagicWordArray { } /** - * Get a regex for matching variables + * Get a regex for matching variables with parameters + * + * @return string */ function getVariableRegex() { return str_replace( "\\$1", "(.*?)", $this->getRegex() ); } /** - * Get an anchored regex for matching variables + * Get a regex anchored to the start of the string that does not match parameters + * + * @return array + */ + function getRegexStart() { + $base = $this->getBaseRegex(); + $newRegex = array( '', '' ); + if ( $base[0] !== '' ) { + $newRegex[0] = "/^(?:{$base[0]})/iuS"; + } + if ( $base[1] !== '' ) { + $newRegex[1] = "/^(?:{$base[1]})/S"; + } + return $newRegex; + } + + /** + * Get an anchored regex for matching variables with parameters + * + * @return array */ function getVariableStartToEndRegex() { $base = $this->getBaseRegex(); @@ -505,6 +748,12 @@ class MagicWordArray { /** * Parse a match array from preg_match + * Returns array(magic word ID, parameter value) + * If there is no parameter value, that element will be false. + * + * @param $m array + * + * @return array */ function parseMatch( $m ) { reset( $m ); @@ -524,17 +773,19 @@ class MagicWordArray { } // This shouldn't happen either throw new MWException( __METHOD__.': parameter not found' ); - return array( false, false ); } /** * Match some text, with parameter capture - * Returns an array with the magic word name in the first element and the + * Returns an array with the magic word name in the first element and the * parameter in the second element. * Both elements are false if there was no match. + * + * @param $text string + * + * @return array */ public function matchVariableStartToEnd( $text ) { - global $wgContLang; $regexes = $this->getVariableStartToEndRegex(); foreach ( $regexes as $regex ) { if ( $regex !== '' ) { @@ -550,6 +801,10 @@ class MagicWordArray { /** * Match some text, without parameter capture * Returns the magic word name, or false if there was no capture + * + * @param $text string + * + * @return string|false */ public function matchStartToEnd( $text ) { $hash = $this->getHash(); @@ -563,4 +818,58 @@ class MagicWordArray { } return false; } + + /** + * Returns an associative array, ID => param value, for all items that match + * Removes the matched items from the input string (passed by reference) + * + * @param $text string + * + * @return array + */ + public function matchAndRemove( &$text ) { + $found = array(); + $regexes = $this->getRegex(); + foreach ( $regexes as $regex ) { + if ( $regex === '' ) { + continue; + } + preg_match_all( $regex, $text, $matches, PREG_SET_ORDER ); + foreach ( $matches as $m ) { + list( $name, $param ) = $this->parseMatch( $m ); + $found[$name] = $param; + } + $text = preg_replace( $regex, '', $text ); + } + return $found; + } + + /** + * Return the ID of the magic word at the start of $text, and remove + * the prefix from $text. + * Return false if no match found and $text is not modified. + * Does not match parameters. + * + * @param $text string + * + * @return int|false + */ + public function matchStartAndRemove( &$text ) { + $regexes = $this->getRegexStart(); + foreach ( $regexes as $regex ) { + if ( $regex === '' ) { + continue; + } + if ( preg_match( $regex, $text, $m ) ) { + list( $id, ) = $this->parseMatch( $m ); + if ( strlen( $m[0] ) >= strlen( $text ) ) { + $text = ''; + } else { + $text = substr( $text, strlen( $m[0] ) ); + } + return $id; + } + } + return false; + } }