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 'currentmonth1',
00040 'currentmonthname',
00041 'currentmonthnamegen',
00042 'currentmonthabbrev',
00043 'currentday',
00044 'currentday2',
00045 'currentdayname',
00046 'currentyear',
00047 'currenttime',
00048 'currenthour',
00049 'localmonth',
00050 'localmonth1',
00051 'localmonthname',
00052 'localmonthnamegen',
00053 'localmonthabbrev',
00054 'localday',
00055 'localday2',
00056 'localdayname',
00057 'localyear',
00058 'localtime',
00059 'localhour',
00060 'numberofarticles',
00061 'numberoffiles',
00062 'numberofedits',
00063 'sitename',
00064 'server',
00065 'servername',
00066 'scriptpath',
00067 'stylepath',
00068 'pagename',
00069 'pagenamee',
00070 'fullpagename',
00071 'fullpagenamee',
00072 'namespace',
00073 'namespacee',
00074 'currentweek',
00075 'currentdow',
00076 'localweek',
00077 'localdow',
00078 'revisionid',
00079 'revisionday',
00080 'revisionday2',
00081 'revisionmonth',
00082 'revisionyear',
00083 'revisiontimestamp',
00084 'revisionuser',
00085 'subpagename',
00086 'subpagenamee',
00087 'talkspace',
00088 'talkspacee',
00089 'subjectspace',
00090 'subjectspacee',
00091 'talkpagename',
00092 'talkpagenamee',
00093 'subjectpagename',
00094 'subjectpagenamee',
00095 'numberofusers',
00096 'numberofactiveusers',
00097 'numberofpages',
00098 'currentversion',
00099 'basepagename',
00100 'basepagenamee',
00101 'currenttimestamp',
00102 'localtimestamp',
00103 'directionmark',
00104 'contentlanguage',
00105 'numberofadmins',
00106 'numberofviews',
00107 );
00108
00109
00110 static public $mCacheTTLs = array (
00111 'currentmonth' => 86400,
00112 'currentmonth1' => 86400,
00113 'currentmonthname' => 86400,
00114 'currentmonthnamegen' => 86400,
00115 'currentmonthabbrev' => 86400,
00116 'currentday' => 3600,
00117 'currentday2' => 3600,
00118 'currentdayname' => 3600,
00119 'currentyear' => 86400,
00120 'currenttime' => 3600,
00121 'currenthour' => 3600,
00122 'localmonth' => 86400,
00123 'localmonth1' => 86400,
00124 'localmonthname' => 86400,
00125 'localmonthnamegen' => 86400,
00126 'localmonthabbrev' => 86400,
00127 'localday' => 3600,
00128 'localday2' => 3600,
00129 'localdayname' => 3600,
00130 'localyear' => 86400,
00131 'localtime' => 3600,
00132 'localhour' => 3600,
00133 'numberofarticles' => 3600,
00134 'numberoffiles' => 3600,
00135 'numberofedits' => 3600,
00136 'currentweek' => 3600,
00137 'currentdow' => 3600,
00138 'localweek' => 3600,
00139 'localdow' => 3600,
00140 'numberofusers' => 3600,
00141 'numberofactiveusers' => 3600,
00142 'numberofpages' => 3600,
00143 'currentversion' => 86400,
00144 'currenttimestamp' => 3600,
00145 'localtimestamp' => 3600,
00146 'pagesinnamespace' => 3600,
00147 'numberofadmins' => 3600,
00148 'numberofviews' => 3600,
00149 'numberingroup' => 3600,
00150 );
00151
00152 static public $mDoubleUnderscoreIDs = array(
00153 'notoc',
00154 'nogallery',
00155 'forcetoc',
00156 'toc',
00157 'noeditsection',
00158 'newsectionlink',
00159 'nonewsectionlink',
00160 'hiddencat',
00161 'index',
00162 'noindex',
00163 'staticredirect',
00164 'notitleconvert',
00165 'nocontentconvert',
00166 );
00167
00168 static public $mSubstIDs = array(
00169 'subst',
00170 'safesubst',
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 ( !isset( self::$mObjects[$id] ) ) {
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
00226 static function getSubstIDs() {
00227 return self::$mSubstIDs;
00228 }
00229
00230
00231 static function getCacheTTL($id) {
00232 if (array_key_exists($id,self::$mCacheTTLs)) {
00233 return self::$mCacheTTLs[$id];
00234 } else {
00235 return -1;
00236 }
00237 }
00238
00240 static function getDoubleUnderscoreArray() {
00241 if ( is_null( self::$mDoubleUnderscoreArray ) ) {
00242 self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs );
00243 }
00244 return self::$mDoubleUnderscoreArray;
00245 }
00246
00251 public static function clearCache() {
00252 self::$mObjects = array();
00253 }
00254
00255 # Initialises this object with an ID
00256 function load( $id ) {
00257 global $wgContLang;
00258 $this->mId = $id;
00259 $wgContLang->getMagic( $this );
00260 if ( !$this->mSynonyms ) {
00261 $this->mSynonyms = array( 'dkjsagfjsgashfajsh' );
00262 #throw new MWException( "Error: invalid magic word '$id'" );
00263 wfDebugLog( 'exception', "Error: invalid magic word '$id'\n" );
00264 }
00265 }
00266
00271 function initRegex() {
00272 #$variableClass = Title::legalChars();
00273 # This was used for matching "$1" variables, but different uses of the feature will have
00274 # different restrictions, which should be checked *after* the MagicWord has been matched,
00275 # not here. - IMSoP
00276
00277 $escSyn = array();
00278 foreach ( $this->mSynonyms as $synonym )
00279
00280 $escSyn[] = preg_quote( $synonym, '/' );
00281 $this->mBaseRegex = implode( '|', $escSyn );
00282
00283 $case = $this->mCaseSensitive ? '' : 'iu';
00284 $this->mRegex = "/{$this->mBaseRegex}/{$case}";
00285 $this->mRegexStart = "/^(?:{$this->mBaseRegex})/{$case}";
00286 $this->mVariableRegex = str_replace( "\\$1", "(.*?)", $this->mRegex );
00287 $this->mVariableStartToEndRegex = str_replace( "\\$1", "(.*?)",
00288 "/^(?:{$this->mBaseRegex})$/{$case}" );
00289 }
00290
00294 function getRegex() {
00295 if ($this->mRegex == '' ) {
00296 $this->initRegex();
00297 }
00298 return $this->mRegex;
00299 }
00300
00306 function getRegexCase() {
00307 if ( $this->mRegex === '' )
00308 $this->initRegex();
00309
00310 return $this->mCaseSensitive ? '' : 'iu';
00311 }
00312
00316 function getRegexStart() {
00317 if ($this->mRegex == '' ) {
00318 $this->initRegex();
00319 }
00320 return $this->mRegexStart;
00321 }
00322
00326 function getBaseRegex() {
00327 if ($this->mRegex == '') {
00328 $this->initRegex();
00329 }
00330 return $this->mBaseRegex;
00331 }
00332
00337 function match( $text ) {
00338 return (bool)preg_match( $this->getRegex(), $text );
00339 }
00340
00345 function matchStart( $text ) {
00346 return (bool)preg_match( $this->getRegexStart(), $text );
00347 }
00348
00355 function matchVariableStartToEnd( $text ) {
00356 $matches = array();
00357 $matchcount = preg_match( $this->getVariableStartToEndRegex(), $text, $matches );
00358 if ( $matchcount == 0 ) {
00359 return null;
00360 } else {
00361 # multiple matched parts (variable match); some will be empty because of
00362 # synonyms. The variable will be the second non-empty one so remove any
00363 # blank elements and re-sort the indices.
00364 # See also bug 6526
00365
00366 $matches = array_values(array_filter($matches));
00367
00368 if ( count($matches) == 1 ) { return $matches[0]; }
00369 else { return $matches[1]; }
00370 }
00371 }
00372
00373
00378 function matchAndRemove( &$text ) {
00379 $this->mFound = false;
00380 $text = preg_replace_callback( $this->getRegex(), array( &$this, 'pregRemoveAndRecord' ), $text );
00381 return $this->mFound;
00382 }
00383
00384 function matchStartAndRemove( &$text ) {
00385 $this->mFound = false;
00386 $text = preg_replace_callback( $this->getRegexStart(), array( &$this, 'pregRemoveAndRecord' ), $text );
00387 return $this->mFound;
00388 }
00389
00394 function pregRemoveAndRecord( ) {
00395 $this->mFound = true;
00396 return '';
00397 }
00398
00402 function replace( $replacement, $subject, $limit=-1 ) {
00403 $res = preg_replace( $this->getRegex(), StringUtils::escapeRegexReplacement( $replacement ), $subject, $limit );
00404 $this->mModified = !($res === $subject);
00405 return $res;
00406 }
00407
00413 function substituteCallback( $text, $callback ) {
00414 $res = preg_replace_callback( $this->getVariableRegex(), $callback, $text );
00415 $this->mModified = !($res === $text);
00416 return $res;
00417 }
00418
00422 function getVariableRegex() {
00423 if ( $this->mVariableRegex == '' ) {
00424 $this->initRegex();
00425 }
00426 return $this->mVariableRegex;
00427 }
00428
00432 function getVariableStartToEndRegex() {
00433 if ( $this->mVariableStartToEndRegex == '' ) {
00434 $this->initRegex();
00435 }
00436 return $this->mVariableStartToEndRegex;
00437 }
00438
00442 function getSynonym( $i ) {
00443 return $this->mSynonyms[$i];
00444 }
00445
00446 function getSynonyms() {
00447 return $this->mSynonyms;
00448 }
00449
00454 function getWasModified(){
00455 return $this->mModified;
00456 }
00457
00465 function replaceMultiple( $magicarr, $subject, &$result ){
00466 $search = array();
00467 $replace = array();
00468 foreach( $magicarr as $id => $replacement ){
00469 $mw = MagicWord::get( $id );
00470 $search[] = $mw->getRegex();
00471 $replace[] = $replacement;
00472 }
00473
00474 $result = preg_replace( $search, $replace, $subject );
00475 return !($result === $subject);
00476 }
00477
00482 function addToArray( &$array, $value ) {
00483 global $wgContLang;
00484 foreach ( $this->mSynonyms as $syn ) {
00485 $array[$wgContLang->lc($syn)] = $value;
00486 }
00487 }
00488
00489 function isCaseSensitive() {
00490 return $this->mCaseSensitive;
00491 }
00492
00493 function getId() {
00494 return $this->mId;
00495 }
00496 }
00497
00502 class MagicWordArray {
00503 var $names = array();
00504 var $hash;
00505 var $baseRegex, $regex;
00506 var $matches;
00507
00508 function __construct( $names = array() ) {
00509 $this->names = $names;
00510 }
00511
00515 public function add( $name ) {
00516 global $wgContLang;
00517 $this->names[] = $name;
00518 $this->hash = $this->baseRegex = $this->regex = null;
00519 }
00520
00524 public function addArray( $names ) {
00525 $this->names = array_merge( $this->names, array_values( $names ) );
00526 $this->hash = $this->baseRegex = $this->regex = null;
00527 }
00528
00532 function getHash() {
00533 if ( is_null( $this->hash ) ) {
00534 global $wgContLang;
00535 $this->hash = array( 0 => array(), 1 => array() );
00536 foreach ( $this->names as $name ) {
00537 $magic = MagicWord::get( $name );
00538 $case = intval( $magic->isCaseSensitive() );
00539 foreach ( $magic->getSynonyms() as $syn ) {
00540 if ( !$case ) {
00541 $syn = $wgContLang->lc( $syn );
00542 }
00543 $this->hash[$case][$syn] = $name;
00544 }
00545 }
00546 }
00547 return $this->hash;
00548 }
00549
00553 function getBaseRegex() {
00554 if ( is_null( $this->baseRegex ) ) {
00555 $this->baseRegex = array( 0 => '', 1 => '' );
00556 foreach ( $this->names as $name ) {
00557 $magic = MagicWord::get( $name );
00558 $case = intval( $magic->isCaseSensitive() );
00559 foreach ( $magic->getSynonyms() as $i => $syn ) {
00560 $group = "(?P<{$i}_{$name}>" . preg_quote( $syn, '/' ) . ')';
00561 if ( $this->baseRegex[$case] === '' ) {
00562 $this->baseRegex[$case] = $group;
00563 } else {
00564 $this->baseRegex[$case] .= '|' . $group;
00565 }
00566 }
00567 }
00568 }
00569 return $this->baseRegex;
00570 }
00571
00575 function getRegex() {
00576 if ( is_null( $this->regex ) ) {
00577 $base = $this->getBaseRegex();
00578 $this->regex = array( '', '' );
00579 if ( $this->baseRegex[0] !== '' ) {
00580 $this->regex[0] = "/{$base[0]}/iuS";
00581 }
00582 if ( $this->baseRegex[1] !== '' ) {
00583 $this->regex[1] = "/{$base[1]}/S";
00584 }
00585 }
00586 return $this->regex;
00587 }
00588
00592 function getVariableRegex() {
00593 return str_replace( "\\$1", "(.*?)", $this->getRegex() );
00594 }
00595
00599 function getRegexStart() {
00600 $base = $this->getBaseRegex();
00601 $newRegex = array( '', '' );
00602 if ( $base[0] !== '' ) {
00603 $newRegex[0] = "/^(?:{$base[0]})/iuS";
00604 }
00605 if ( $base[1] !== '' ) {
00606 $newRegex[1] = "/^(?:{$base[1]})/S";
00607 }
00608 return $newRegex;
00609 }
00610
00614 function getVariableStartToEndRegex() {
00615 $base = $this->getBaseRegex();
00616 $newRegex = array( '', '' );
00617 if ( $base[0] !== '' ) {
00618 $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
00619 }
00620 if ( $base[1] !== '' ) {
00621 $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
00622 }
00623 return $newRegex;
00624 }
00625
00631 function parseMatch( $m ) {
00632 reset( $m );
00633 while ( list( $key, $value ) = each( $m ) ) {
00634 if ( $key === 0 || $value === '' ) {
00635 continue;
00636 }
00637 $parts = explode( '_', $key, 2 );
00638 if ( count( $parts ) != 2 ) {
00639
00640
00641 throw new MWException( __METHOD__ . ': bad parameter name' );
00642 }
00643 list( , $magicName ) = $parts;
00644 $paramValue = next( $m );
00645 return array( $magicName, $paramValue );
00646 }
00647
00648 throw new MWException( __METHOD__.': parameter not found' );
00649 return array( false, false );
00650 }
00651
00658 public function matchVariableStartToEnd( $text ) {
00659 global $wgContLang;
00660 $regexes = $this->getVariableStartToEndRegex();
00661 foreach ( $regexes as $regex ) {
00662 if ( $regex !== '' ) {
00663 $m = false;
00664 if ( preg_match( $regex, $text, $m ) ) {
00665 return $this->parseMatch( $m );
00666 }
00667 }
00668 }
00669 return array( false, false );
00670 }
00671
00676 public function matchStartToEnd( $text ) {
00677 $hash = $this->getHash();
00678 if ( isset( $hash[1][$text] ) ) {
00679 return $hash[1][$text];
00680 }
00681 global $wgContLang;
00682 $lc = $wgContLang->lc( $text );
00683 if ( isset( $hash[0][$lc] ) ) {
00684 return $hash[0][$lc];
00685 }
00686 return false;
00687 }
00688
00693 public function matchAndRemove( &$text ) {
00694 $found = array();
00695 $regexes = $this->getRegex();
00696 foreach ( $regexes as $regex ) {
00697 if ( $regex === '' ) {
00698 continue;
00699 }
00700 preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
00701 foreach ( $matches as $m ) {
00702 list( $name, $param ) = $this->parseMatch( $m );
00703 $found[$name] = $param;
00704 }
00705 $text = preg_replace( $regex, '', $text );
00706 }
00707 return $found;
00708 }
00709
00716 public function matchStartAndRemove( &$text ) {
00717 $regexes = $this->getRegexStart();
00718 foreach ( $regexes as $regex ) {
00719 if ( $regex === '' ) {
00720 continue;
00721 }
00722 if ( preg_match( $regex, $text, $m ) ) {
00723 list( $id, $param ) = $this->parseMatch( $m );
00724 if ( strlen( $m[0] ) >= strlen( $text ) ) {
00725 $text = '';
00726 } else {
00727 $text = substr( $text, strlen( $m[0] ) );
00728 }
00729 return $id;
00730 }
00731 }
00732 return false;
00733 }
00734 }