00001 <?php
00028 class MagicWord {
00032 var $mId, $mSynonyms, $mCaseSensitive, $mRegex;
00033 var $mRegexStart, $mBaseRegex, $mVariableRegex;
00034 var $mModified, $mFound;
00035
00036 static public $mVariableIDsInitialised = false;
00037 static public $mVariableIDs = array(
00038 'currentmonth',
00039 'currentmonthname',
00040 'currentmonthnamegen',
00041 'currentmonthabbrev',
00042 'currentday',
00043 'currentday2',
00044 'currentdayname',
00045 'currentyear',
00046 'currenttime',
00047 'currenthour',
00048 'localmonth',
00049 'localmonthname',
00050 'localmonthnamegen',
00051 'localmonthabbrev',
00052 'localday',
00053 'localday2',
00054 'localdayname',
00055 'localyear',
00056 'localtime',
00057 'localhour',
00058 'numberofarticles',
00059 'numberoffiles',
00060 'numberofedits',
00061 'sitename',
00062 'server',
00063 'servername',
00064 'scriptpath',
00065 'pagename',
00066 'pagenamee',
00067 'fullpagename',
00068 'fullpagenamee',
00069 'namespace',
00070 'namespacee',
00071 'currentweek',
00072 'currentdow',
00073 'localweek',
00074 'localdow',
00075 'revisionid',
00076 'revisionday',
00077 'revisionday2',
00078 'revisionmonth',
00079 'revisionyear',
00080 'revisiontimestamp',
00081 'revisionuser',
00082 'subpagename',
00083 'subpagenamee',
00084 'displaytitle',
00085 'talkspace',
00086 'talkspacee',
00087 'subjectspace',
00088 'subjectspacee',
00089 'talkpagename',
00090 'talkpagenamee',
00091 'subjectpagename',
00092 'subjectpagenamee',
00093 'numberofusers',
00094 'numberofactiveusers',
00095 'newsectionlink',
00096 'nonewsectionlink',
00097 'numberofpages',
00098 'currentversion',
00099 'basepagename',
00100 'basepagenamee',
00101 'urlencode',
00102 'currenttimestamp',
00103 'localtimestamp',
00104 'directionmark',
00105 'language',
00106 'contentlanguage',
00107 'pagesinnamespace',
00108 'numberofadmins',
00109 'numberofviews',
00110 'defaultsort',
00111 'pagesincategory',
00112 'index',
00113 'noindex',
00114 'numberingroup',
00115 );
00116
00117
00118 static public $mCacheTTLs = array (
00119 'currentmonth' => 86400,
00120 'currentmonthname' => 86400,
00121 'currentmonthnamegen' => 86400,
00122 'currentmonthabbrev' => 86400,
00123 'currentday' => 3600,
00124 'currentday2' => 3600,
00125 'currentdayname' => 3600,
00126 'currentyear' => 86400,
00127 'currenttime' => 3600,
00128 'currenthour' => 3600,
00129 'localmonth' => 86400,
00130 'localmonthname' => 86400,
00131 'localmonthnamegen' => 86400,
00132 'localmonthabbrev' => 86400,
00133 'localday' => 3600,
00134 'localday2' => 3600,
00135 'localdayname' => 3600,
00136 'localyear' => 86400,
00137 'localtime' => 3600,
00138 'localhour' => 3600,
00139 'numberofarticles' => 3600,
00140 'numberoffiles' => 3600,
00141 'numberofedits' => 3600,
00142 'currentweek' => 3600,
00143 'currentdow' => 3600,
00144 'localweek' => 3600,
00145 'localdow' => 3600,
00146 'numberofusers' => 3600,
00147 'numberofactiveusers' => 3600,
00148 'numberofpages' => 3600,
00149 'currentversion' => 86400,
00150 'currenttimestamp' => 3600,
00151 'localtimestamp' => 3600,
00152 'pagesinnamespace' => 3600,
00153 'numberofadmins' => 3600,
00154 'numberofviews' => 3600,
00155 'numberingroup' => 3600,
00156 );
00157
00158 static public $mDoubleUnderscoreIDs = array(
00159 'notoc',
00160 'nogallery',
00161 'forcetoc',
00162 'toc',
00163 'noeditsection',
00164 'newsectionlink',
00165 'nonewsectionlink',
00166 'hiddencat',
00167 'index',
00168 'noindex',
00169 'staticredirect',
00170 );
00171
00172
00173 static public $mObjects = array();
00174 static public $mDoubleUnderscoreArray = null;
00175
00178 function __construct($id = 0, $syn = '', $cs = false) {
00179 $this->mId = $id;
00180 $this->mSynonyms = (array)$syn;
00181 $this->mCaseSensitive = $cs;
00182 $this->mRegex = '';
00183 $this->mRegexStart = '';
00184 $this->mVariableRegex = '';
00185 $this->mVariableStartToEndRegex = '';
00186 $this->mModified = false;
00187 }
00188
00193 static function &get( $id ) {
00194 wfProfileIn( __METHOD__ );
00195 if (!array_key_exists( $id, self::$mObjects ) ) {
00196 $mw = new MagicWord();
00197 $mw->load( $id );
00198 self::$mObjects[$id] = $mw;
00199 }
00200 wfProfileOut( __METHOD__ );
00201 return self::$mObjects[$id];
00202 }
00203
00207 static function getVariableIDs() {
00208 if ( !self::$mVariableIDsInitialised ) {
00209 # Deprecated constant definition hook, available for extensions that need it
00210 $magicWords = array();
00211 wfRunHooks( 'MagicWordMagicWords', array( &$magicWords ) );
00212 foreach ( $magicWords as $word ) {
00213 define( $word, $word );
00214 }
00215
00216 # Get variable IDs
00217 wfRunHooks( 'MagicWordwgVariableIDs', array( &self::$mVariableIDs ) );
00218 self::$mVariableIDsInitialised = true;
00219 }
00220 return self::$mVariableIDs;
00221 }
00222
00223
00224 static function getCacheTTL($id) {
00225 if (array_key_exists($id,self::$mCacheTTLs)) {
00226 return self::$mCacheTTLs[$id];
00227 } else {
00228 return -1;
00229 }
00230 }
00231
00233 static function getDoubleUnderscoreArray() {
00234 if ( is_null( self::$mDoubleUnderscoreArray ) ) {
00235 self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs );
00236 }
00237 return self::$mDoubleUnderscoreArray;
00238 }
00239
00240 # Initialises this object with an ID
00241 function load( $id ) {
00242 global $wgContLang;
00243 $this->mId = $id;
00244 $wgContLang->getMagic( $this );
00245 if ( !$this->mSynonyms ) {
00246 $this->mSynonyms = array( 'dkjsagfjsgashfajsh' );
00247 #throw new MWException( "Error: invalid magic word '$id'" );
00248 wfDebugLog( 'exception', "Error: invalid magic word '$id'\n" );
00249 }
00250 }
00251
00256 function initRegex() {
00257 #$variableClass = Title::legalChars();
00258 # This was used for matching "$1" variables, but different uses of the feature will have
00259 # different restrictions, which should be checked *after* the MagicWord has been matched,
00260 # not here. - IMSoP
00261
00262 $escSyn = array();
00263 foreach ( $this->mSynonyms as $synonym )
00264
00265 $escSyn[] = preg_quote( $synonym, '/' );
00266 $this->mBaseRegex = implode( '|', $escSyn );
00267
00268 $case = $this->mCaseSensitive ? '' : 'iu';
00269 $this->mRegex = "/{$this->mBaseRegex}/{$case}";
00270 $this->mRegexStart = "/^(?:{$this->mBaseRegex})/{$case}";
00271 $this->mVariableRegex = str_replace( "\\$1", "(.*?)", $this->mRegex );
00272 $this->mVariableStartToEndRegex = str_replace( "\\$1", "(.*?)",
00273 "/^(?:{$this->mBaseRegex})$/{$case}" );
00274 }
00275
00279 function getRegex() {
00280 if ($this->mRegex == '' ) {
00281 $this->initRegex();
00282 }
00283 return $this->mRegex;
00284 }
00285
00291 function getRegexCase() {
00292 if ( $this->mRegex === '' )
00293 $this->initRegex();
00294
00295 return $this->mCaseSensitive ? '' : 'iu';
00296 }
00297
00301 function getRegexStart() {
00302 if ($this->mRegex == '' ) {
00303 $this->initRegex();
00304 }
00305 return $this->mRegexStart;
00306 }
00307
00311 function getBaseRegex() {
00312 if ($this->mRegex == '') {
00313 $this->initRegex();
00314 }
00315 return $this->mBaseRegex;
00316 }
00317
00322 function match( $text ) {
00323 return preg_match( $this->getRegex(), $text );
00324 }
00325
00330 function matchStart( $text ) {
00331 return preg_match( $this->getRegexStart(), $text );
00332 }
00333
00340 function matchVariableStartToEnd( $text ) {
00341 $matches = array();
00342 $matchcount = preg_match( $this->getVariableStartToEndRegex(), $text, $matches );
00343 if ( $matchcount == 0 ) {
00344 return NULL;
00345 } else {
00346 # multiple matched parts (variable match); some will be empty because of
00347 # synonyms. The variable will be the second non-empty one so remove any
00348 # blank elements and re-sort the indices.
00349 # See also bug 6526
00350
00351 $matches = array_values(array_filter($matches));
00352
00353 if ( count($matches) == 1 ) { return $matches[0]; }
00354 else { return $matches[1]; }
00355 }
00356 }
00357
00358
00363 function matchAndRemove( &$text ) {
00364 $this->mFound = false;
00365 $text = preg_replace_callback( $this->getRegex(), array( &$this, 'pregRemoveAndRecord' ), $text );
00366 return $this->mFound;
00367 }
00368
00369 function matchStartAndRemove( &$text ) {
00370 $this->mFound = false;
00371 $text = preg_replace_callback( $this->getRegexStart(), array( &$this, 'pregRemoveAndRecord' ), $text );
00372 return $this->mFound;
00373 }
00374
00379 function pregRemoveAndRecord( ) {
00380 $this->mFound = true;
00381 return '';
00382 }
00383
00387 function replace( $replacement, $subject, $limit=-1 ) {
00388 $res = preg_replace( $this->getRegex(), StringUtils::escapeRegexReplacement( $replacement ), $subject, $limit );
00389 $this->mModified = !($res === $subject);
00390 return $res;
00391 }
00392
00398 function substituteCallback( $text, $callback ) {
00399 $res = preg_replace_callback( $this->getVariableRegex(), $callback, $text );
00400 $this->mModified = !($res === $text);
00401 return $res;
00402 }
00403
00407 function getVariableRegex() {
00408 if ( $this->mVariableRegex == '' ) {
00409 $this->initRegex();
00410 }
00411 return $this->mVariableRegex;
00412 }
00413
00417 function getVariableStartToEndRegex() {
00418 if ( $this->mVariableStartToEndRegex == '' ) {
00419 $this->initRegex();
00420 }
00421 return $this->mVariableStartToEndRegex;
00422 }
00423
00427 function getSynonym( $i ) {
00428 return $this->mSynonyms[$i];
00429 }
00430
00431 function getSynonyms() {
00432 return $this->mSynonyms;
00433 }
00434
00439 function getWasModified(){
00440 return $this->mModified;
00441 }
00442
00450 function replaceMultiple( $magicarr, $subject, &$result ){
00451 $search = array();
00452 $replace = array();
00453 foreach( $magicarr as $id => $replacement ){
00454 $mw = MagicWord::get( $id );
00455 $search[] = $mw->getRegex();
00456 $replace[] = $replacement;
00457 }
00458
00459 $result = preg_replace( $search, $replace, $subject );
00460 return !($result === $subject);
00461 }
00462
00467 function addToArray( &$array, $value ) {
00468 global $wgContLang;
00469 foreach ( $this->mSynonyms as $syn ) {
00470 $array[$wgContLang->lc($syn)] = $value;
00471 }
00472 }
00473
00474 function isCaseSensitive() {
00475 return $this->mCaseSensitive;
00476 }
00477
00478 function getId() {
00479 return $this->mId;
00480 }
00481 }
00482
00487 class MagicWordArray {
00488 var $names = array();
00489 var $hash;
00490 var $baseRegex, $regex;
00491 var $matches;
00492
00493 function __construct( $names = array() ) {
00494 $this->names = $names;
00495 }
00496
00500 public function add( $name ) {
00501 global $wgContLang;
00502 $this->names[] = $name;
00503 $this->hash = $this->baseRegex = $this->regex = null;
00504 }
00505
00509 public function addArray( $names ) {
00510 $this->names = array_merge( $this->names, array_values( $names ) );
00511 $this->hash = $this->baseRegex = $this->regex = null;
00512 }
00513
00517 function getHash() {
00518 if ( is_null( $this->hash ) ) {
00519 global $wgContLang;
00520 $this->hash = array( 0 => array(), 1 => array() );
00521 foreach ( $this->names as $name ) {
00522 $magic = MagicWord::get( $name );
00523 $case = intval( $magic->isCaseSensitive() );
00524 foreach ( $magic->getSynonyms() as $syn ) {
00525 if ( !$case ) {
00526 $syn = $wgContLang->lc( $syn );
00527 }
00528 $this->hash[$case][$syn] = $name;
00529 }
00530 }
00531 }
00532 return $this->hash;
00533 }
00534
00538 function getBaseRegex() {
00539 if ( is_null( $this->baseRegex ) ) {
00540 $this->baseRegex = array( 0 => '', 1 => '' );
00541 foreach ( $this->names as $name ) {
00542 $magic = MagicWord::get( $name );
00543 $case = intval( $magic->isCaseSensitive() );
00544 foreach ( $magic->getSynonyms() as $i => $syn ) {
00545 $group = "(?P<{$i}_{$name}>" . preg_quote( $syn, '/' ) . ')';
00546 if ( $this->baseRegex[$case] === '' ) {
00547 $this->baseRegex[$case] = $group;
00548 } else {
00549 $this->baseRegex[$case] .= '|' . $group;
00550 }
00551 }
00552 }
00553 }
00554 return $this->baseRegex;
00555 }
00556
00560 function getRegex() {
00561 if ( is_null( $this->regex ) ) {
00562 $base = $this->getBaseRegex();
00563 $this->regex = array( '', '' );
00564 if ( $this->baseRegex[0] !== '' ) {
00565 $this->regex[0] = "/{$base[0]}/iuS";
00566 }
00567 if ( $this->baseRegex[1] !== '' ) {
00568 $this->regex[1] = "/{$base[1]}/S";
00569 }
00570 }
00571 return $this->regex;
00572 }
00573
00577 function getVariableRegex() {
00578 return str_replace( "\\$1", "(.*?)", $this->getRegex() );
00579 }
00580
00584 function getVariableStartToEndRegex() {
00585 $base = $this->getBaseRegex();
00586 $newRegex = array( '', '' );
00587 if ( $base[0] !== '' ) {
00588 $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
00589 }
00590 if ( $base[1] !== '' ) {
00591 $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
00592 }
00593 return $newRegex;
00594 }
00595
00601 function parseMatch( $m ) {
00602 reset( $m );
00603 while ( list( $key, $value ) = each( $m ) ) {
00604 if ( $key === 0 || $value === '' ) {
00605 continue;
00606 }
00607 $parts = explode( '_', $key, 2 );
00608 if ( count( $parts ) != 2 ) {
00609
00610
00611 throw new MWException( __METHOD__ . ': bad parameter name' );
00612 }
00613 list( , $magicName ) = $parts;
00614 $paramValue = next( $m );
00615 return array( $magicName, $paramValue );
00616 }
00617
00618 throw new MWException( __METHOD__.': parameter not found' );
00619 return array( false, false );
00620 }
00621
00628 public function matchVariableStartToEnd( $text ) {
00629 global $wgContLang;
00630 $regexes = $this->getVariableStartToEndRegex();
00631 foreach ( $regexes as $regex ) {
00632 if ( $regex !== '' ) {
00633 $m = false;
00634 if ( preg_match( $regex, $text, $m ) ) {
00635 return $this->parseMatch( $m );
00636 }
00637 }
00638 }
00639 return array( false, false );
00640 }
00641
00646 public function matchStartToEnd( $text ) {
00647 $hash = $this->getHash();
00648 if ( isset( $hash[1][$text] ) ) {
00649 return $hash[1][$text];
00650 }
00651 global $wgContLang;
00652 $lc = $wgContLang->lc( $text );
00653 if ( isset( $hash[0][$lc] ) ) {
00654 return $hash[0][$lc];
00655 }
00656 return false;
00657 }
00658
00663 public function matchAndRemove( &$text ) {
00664 $found = array();
00665 $regexes = $this->getRegex();
00666 foreach ( $regexes as $regex ) {
00667 if ( $regex === '' ) {
00668 continue;
00669 }
00670 preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
00671 foreach ( $matches as $m ) {
00672 list( $name, $param ) = $this->parseMatch( $m );
00673 $found[$name] = $param;
00674 }
00675 $text = preg_replace( $regex, '', $text );
00676 }
00677 return $found;
00678 }
00679 }