[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> MagicWord.php (source)

   1  <?php
   2  /**
   3   * File for magic words.
   4   *
   5   * See docs/magicword.txt.
   6   *
   7   * This program is free software; you can redistribute it and/or modify
   8   * it under the terms of the GNU General Public License as published by
   9   * the Free Software Foundation; either version 2 of the License, or
  10   * (at your option) any later version.
  11   *
  12   * This program is distributed in the hope that it will be useful,
  13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15   * GNU General Public License for more details.
  16   *
  17   * You should have received a copy of the GNU General Public License along
  18   * with this program; if not, write to the Free Software Foundation, Inc.,
  19   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20   * http://www.gnu.org/copyleft/gpl.html
  21   *
  22   * @file
  23   * @ingroup Parser
  24   */
  25  
  26  /**
  27   * This class encapsulates "magic words" such as "#redirect", __NOTOC__, etc.
  28   *
  29   * @par Usage:
  30   * @code
  31   *     if (MagicWord::get( 'redirect' )->match( $text ) ) {
  32   *       // some code
  33   *     }
  34   * @endcode
  35   *
  36   * Possible future improvements:
  37   *   * Simultaneous searching for a number of magic words
  38   *   * MagicWord::$mObjects in shared memory
  39   *
  40   * Please avoid reading the data out of one of these objects and then writing
  41   * special case code. If possible, add another match()-like function here.
  42   *
  43   * To add magic words in an extension, use $magicWords in a file listed in
  44   * $wgExtensionMessagesFiles[].
  45   *
  46   * @par Example:
  47   * @code
  48   * $magicWords = array();
  49   *
  50   * $magicWords['en'] = array(
  51   *     'magicwordkey' => array( 0, 'case_insensitive_magic_word' ),
  52   *     'magicwordkey2' => array( 1, 'CASE_sensitive_magic_word2' ),
  53   * );
  54   * @endcode
  55   *
  56   * For magic words which are also Parser variables, add a MagicWordwgVariableIDs
  57   * hook. Use string keys.
  58   *
  59   * @ingroup Parser
  60   */
  61  class MagicWord {
  62      /**#@-*/
  63  
  64      /** @var int */
  65      public $mId;
  66  
  67      /** @var array */
  68      public $mSynonyms;
  69  
  70      /** @var bool */
  71      public $mCaseSensitive;
  72  
  73      /** @var string */
  74      private $mRegex = '';
  75  
  76      /** @var string */
  77      private $mRegexStart = '';
  78  
  79      /** @var string */
  80      private $mRegexStartToEnd = '';
  81  
  82      /** @var string */
  83      private $mBaseRegex = '';
  84  
  85      /** @var string */
  86      private $mVariableRegex = '';
  87  
  88      /** @var string */
  89      private $mVariableStartToEndRegex = '';
  90  
  91      /** @var bool */
  92      private $mModified = false;
  93  
  94      /** @var bool */
  95      private $mFound = false;
  96  
  97      static public $mVariableIDsInitialised = false;
  98      static public $mVariableIDs = array(
  99          '!',
 100          'currentmonth',
 101          'currentmonth1',
 102          'currentmonthname',
 103          'currentmonthnamegen',
 104          'currentmonthabbrev',
 105          'currentday',
 106          'currentday2',
 107          'currentdayname',
 108          'currentyear',
 109          'currenttime',
 110          'currenthour',
 111          'localmonth',
 112          'localmonth1',
 113          'localmonthname',
 114          'localmonthnamegen',
 115          'localmonthabbrev',
 116          'localday',
 117          'localday2',
 118          'localdayname',
 119          'localyear',
 120          'localtime',
 121          'localhour',
 122          'numberofarticles',
 123          'numberoffiles',
 124          'numberofedits',
 125          'articlepath',
 126          'pageid',
 127          'sitename',
 128          'server',
 129          'servername',
 130          'scriptpath',
 131          'stylepath',
 132          'pagename',
 133          'pagenamee',
 134          'fullpagename',
 135          'fullpagenamee',
 136          'namespace',
 137          'namespacee',
 138          'namespacenumber',
 139          'currentweek',
 140          'currentdow',
 141          'localweek',
 142          'localdow',
 143          'revisionid',
 144          'revisionday',
 145          'revisionday2',
 146          'revisionmonth',
 147          'revisionmonth1',
 148          'revisionyear',
 149          'revisiontimestamp',
 150          'revisionuser',
 151          'revisionsize',
 152          'subpagename',
 153          'subpagenamee',
 154          'talkspace',
 155          'talkspacee',
 156          'subjectspace',
 157          'subjectspacee',
 158          'talkpagename',
 159          'talkpagenamee',
 160          'subjectpagename',
 161          'subjectpagenamee',
 162          'numberofusers',
 163          'numberofactiveusers',
 164          'numberofpages',
 165          'currentversion',
 166          'rootpagename',
 167          'rootpagenamee',
 168          'basepagename',
 169          'basepagenamee',
 170          'currenttimestamp',
 171          'localtimestamp',
 172          'directionmark',
 173          'contentlanguage',
 174          'numberofadmins',
 175          'numberofviews',
 176          'cascadingsources',
 177      );
 178  
 179      /* Array of caching hints for ParserCache */
 180      static public $mCacheTTLs = array(
 181          'currentmonth' => 86400,
 182          'currentmonth1' => 86400,
 183          'currentmonthname' => 86400,
 184          'currentmonthnamegen' => 86400,
 185          'currentmonthabbrev' => 86400,
 186          'currentday' => 3600,
 187          'currentday2' => 3600,
 188          'currentdayname' => 3600,
 189          'currentyear' => 86400,
 190          'currenttime' => 3600,
 191          'currenthour' => 3600,
 192          'localmonth' => 86400,
 193          'localmonth1' => 86400,
 194          'localmonthname' => 86400,
 195          'localmonthnamegen' => 86400,
 196          'localmonthabbrev' => 86400,
 197          'localday' => 3600,
 198          'localday2' => 3600,
 199          'localdayname' => 3600,
 200          'localyear' => 86400,
 201          'localtime' => 3600,
 202          'localhour' => 3600,
 203          'numberofarticles' => 3600,
 204          'numberoffiles' => 3600,
 205          'numberofedits' => 3600,
 206          'currentweek' => 3600,
 207          'currentdow' => 3600,
 208          'localweek' => 3600,
 209          'localdow' => 3600,
 210          'numberofusers' => 3600,
 211          'numberofactiveusers' => 3600,
 212          'numberofpages' => 3600,
 213          'currentversion' => 86400,
 214          'currenttimestamp' => 3600,
 215          'localtimestamp' => 3600,
 216          'pagesinnamespace' => 3600,
 217          'numberofadmins' => 3600,
 218          'numberofviews' => 3600,
 219          'numberingroup' => 3600,
 220      );
 221  
 222      static public $mDoubleUnderscoreIDs = array(
 223          'notoc',
 224          'nogallery',
 225          'forcetoc',
 226          'toc',
 227          'noeditsection',
 228          'newsectionlink',
 229          'nonewsectionlink',
 230          'hiddencat',
 231          'index',
 232          'noindex',
 233          'staticredirect',
 234          'notitleconvert',
 235          'nocontentconvert',
 236      );
 237  
 238      static public $mSubstIDs = array(
 239          'subst',
 240          'safesubst',
 241      );
 242  
 243      static public $mObjects = array();
 244      static public $mDoubleUnderscoreArray = null;
 245  
 246      /**#@-*/
 247  
 248  	function __construct( $id = 0, $syn = array(), $cs = false ) {
 249          $this->mId = $id;
 250          $this->mSynonyms = (array)$syn;
 251          $this->mCaseSensitive = $cs;
 252      }
 253  
 254      /**
 255       * Factory: creates an object representing an ID
 256       *
 257       * @param int $id
 258       *
 259       * @return MagicWord
 260       */
 261      static function &get( $id ) {
 262          if ( !isset( self::$mObjects[$id] ) ) {
 263              $mw = new MagicWord();
 264              $mw->load( $id );
 265              self::$mObjects[$id] = $mw;
 266          }
 267          return self::$mObjects[$id];
 268      }
 269  
 270      /**
 271       * Get an array of parser variable IDs
 272       *
 273       * @return array
 274       */
 275  	static function getVariableIDs() {
 276          if ( !self::$mVariableIDsInitialised ) {
 277              # Get variable IDs
 278              wfRunHooks( 'MagicWordwgVariableIDs', array( &self::$mVariableIDs ) );
 279              self::$mVariableIDsInitialised = true;
 280          }
 281          return self::$mVariableIDs;
 282      }
 283  
 284      /**
 285       * Get an array of parser substitution modifier IDs
 286       * @return array
 287       */
 288  	static function getSubstIDs() {
 289          return self::$mSubstIDs;
 290      }
 291  
 292      /**
 293       * Allow external reads of TTL array
 294       *
 295       * @param int $id
 296       * @return int
 297       */
 298  	static function getCacheTTL( $id ) {
 299          if ( array_key_exists( $id, self::$mCacheTTLs ) ) {
 300              return self::$mCacheTTLs[$id];
 301          } else {
 302              return -1;
 303          }
 304      }
 305  
 306      /**
 307       * Get a MagicWordArray of double-underscore entities
 308       *
 309       * @return MagicWordArray
 310       */
 311  	static function getDoubleUnderscoreArray() {
 312          if ( is_null( self::$mDoubleUnderscoreArray ) ) {
 313              wfRunHooks( 'GetDoubleUnderscoreIDs', array( &self::$mDoubleUnderscoreIDs ) );
 314              self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs );
 315          }
 316          return self::$mDoubleUnderscoreArray;
 317      }
 318  
 319      /**
 320       * Clear the self::$mObjects variable
 321       * For use in parser tests
 322       */
 323  	public static function clearCache() {
 324          self::$mObjects = array();
 325      }
 326  
 327      /**
 328       * Initialises this object with an ID
 329       *
 330       * @param int $id
 331       * @throws MWException
 332       */
 333  	function load( $id ) {
 334          global $wgContLang;
 335          wfProfileIn( __METHOD__ );
 336          $this->mId = $id;
 337          $wgContLang->getMagic( $this );
 338          if ( !$this->mSynonyms ) {
 339              $this->mSynonyms = array( 'brionmademeputthishere' );
 340              wfProfileOut( __METHOD__ );
 341              throw new MWException( "Error: invalid magic word '$id'" );
 342          }
 343          wfProfileOut( __METHOD__ );
 344      }
 345  
 346      /**
 347       * Preliminary initialisation
 348       * @private
 349       */
 350  	function initRegex() {
 351          // Sort the synonyms by length, descending, so that the longest synonym
 352          // matches in precedence to the shortest
 353          $synonyms = $this->mSynonyms;
 354          usort( $synonyms, array( $this, 'compareStringLength' ) );
 355  
 356          $escSyn = array();
 357          foreach ( $synonyms as $synonym ) {
 358              // In case a magic word contains /, like that's going to happen;)
 359              $escSyn[] = preg_quote( $synonym, '/' );
 360          }
 361          $this->mBaseRegex = implode( '|', $escSyn );
 362  
 363          $case = $this->mCaseSensitive ? '' : 'iu';
 364          $this->mRegex = "/{$this->mBaseRegex}/{$case}";
 365          $this->mRegexStart = "/^(?:{$this->mBaseRegex})/{$case}";
 366          $this->mRegexStartToEnd = "/^(?:{$this->mBaseRegex})$/{$case}";
 367          $this->mVariableRegex = str_replace( "\\$1", "(.*?)", $this->mRegex );
 368          $this->mVariableStartToEndRegex = str_replace( "\\$1", "(.*?)",
 369              "/^(?:{$this->mBaseRegex})$/{$case}" );
 370      }
 371  
 372      /**
 373       * A comparison function that returns -1, 0 or 1 depending on whether the
 374       * first string is longer, the same length or shorter than the second
 375       * string.
 376       *
 377       * @param string $s1
 378       * @param string $s2
 379       *
 380       * @return int
 381       */
 382  	function compareStringLength( $s1, $s2 ) {
 383          $l1 = strlen( $s1 );
 384          $l2 = strlen( $s2 );
 385          if ( $l1 < $l2 ) {
 386              return 1;
 387          } elseif ( $l1 > $l2 ) {
 388              return -1;
 389          } else {
 390              return 0;
 391          }
 392      }
 393  
 394      /**
 395       * Gets a regex representing matching the word
 396       *
 397       * @return string
 398       */
 399  	function getRegex() {
 400          if ( $this->mRegex == '' ) {
 401              $this->initRegex();
 402          }
 403          return $this->mRegex;
 404      }
 405  
 406      /**
 407       * Gets the regexp case modifier to use, i.e. i or nothing, to be used if
 408       * one is using MagicWord::getBaseRegex(), otherwise it'll be included in
 409       * the complete expression
 410       *
 411       * @return string
 412       */
 413  	function getRegexCase() {
 414          if ( $this->mRegex === '' ) {
 415              $this->initRegex();
 416          }
 417  
 418          return $this->mCaseSensitive ? '' : 'iu';
 419      }
 420  
 421      /**
 422       * Gets a regex matching the word, if it is at the string start
 423       *
 424       * @return string
 425       */
 426  	function getRegexStart() {
 427          if ( $this->mRegex == '' ) {
 428              $this->initRegex();
 429          }
 430          return $this->mRegexStart;
 431      }
 432  
 433      /**
 434       * Gets a regex matching the word from start to end of a string
 435       *
 436       * @return string
 437       * @since 1.23
 438       */
 439  	function getRegexStartToEnd() {
 440          if ( $this->mRegexStartToEnd == '' ) {
 441              $this->initRegex();
 442          }
 443          return $this->mRegexStartToEnd;
 444      }
 445  
 446      /**
 447       * regex without the slashes and what not
 448       *
 449       * @return string
 450       */
 451  	function getBaseRegex() {
 452          if ( $this->mRegex == '' ) {
 453              $this->initRegex();
 454          }
 455          return $this->mBaseRegex;
 456      }
 457  
 458      /**
 459       * Returns true if the text contains the word
 460       *
 461       * @param string $text
 462       *
 463       * @return bool
 464       */
 465  	function match( $text ) {
 466          return (bool)preg_match( $this->getRegex(), $text );
 467      }
 468  
 469      /**
 470       * Returns true if the text starts with the word
 471       *
 472       * @param string $text
 473       *
 474       * @return bool
 475       */
 476  	function matchStart( $text ) {
 477          return (bool)preg_match( $this->getRegexStart(), $text );
 478      }
 479  
 480      /**
 481       * Returns true if the text matched the word
 482       *
 483       * @param string $text
 484       *
 485       * @return bool
 486       * @since 1.23
 487       */
 488  	function matchStartToEnd( $text ) {
 489          return (bool)preg_match( $this->getRegexStartToEnd(), $text );
 490      }
 491  
 492      /**
 493       * Returns NULL if there's no match, the value of $1 otherwise
 494       * The return code is the matched string, if there's no variable
 495       * part in the regex and the matched variable part ($1) if there
 496       * is one.
 497       *
 498       * @param string $text
 499       *
 500       * @return string
 501       */
 502  	function matchVariableStartToEnd( $text ) {
 503          $matches = array();
 504          $matchcount = preg_match( $this->getVariableStartToEndRegex(), $text, $matches );
 505          if ( $matchcount == 0 ) {
 506              return null;
 507          } else {
 508              # multiple matched parts (variable match); some will be empty because of
 509              # synonyms. The variable will be the second non-empty one so remove any
 510              # blank elements and re-sort the indices.
 511              # See also bug 6526
 512  
 513              $matches = array_values( array_filter( $matches ) );
 514  
 515              if ( count( $matches ) == 1 ) {
 516                  return $matches[0];
 517              } else {
 518                  return $matches[1];
 519              }
 520          }
 521      }
 522  
 523      /**
 524       * Returns true if the text matches the word, and alters the
 525       * input string, removing all instances of the word
 526       *
 527       * @param string $text
 528       *
 529       * @return bool
 530       */
 531  	function matchAndRemove( &$text ) {
 532          $this->mFound = false;
 533          $text = preg_replace_callback(
 534              $this->getRegex(),
 535              array( &$this, 'pregRemoveAndRecord' ),
 536              $text
 537          );
 538  
 539          return $this->mFound;
 540      }
 541  
 542      /**
 543       * @param string $text
 544       * @return bool
 545       */
 546  	function matchStartAndRemove( &$text ) {
 547          $this->mFound = false;
 548          $text = preg_replace_callback(
 549              $this->getRegexStart(),
 550              array( &$this, 'pregRemoveAndRecord' ),
 551              $text
 552          );
 553  
 554          return $this->mFound;
 555      }
 556  
 557      /**
 558       * Used in matchAndRemove()
 559       *
 560       * @return string
 561       */
 562  	function pregRemoveAndRecord() {
 563          $this->mFound = true;
 564          return '';
 565      }
 566  
 567      /**
 568       * Replaces the word with something else
 569       *
 570       * @param string $replacement
 571       * @param string $subject
 572       * @param int $limit
 573       *
 574       * @return string
 575       */
 576  	function replace( $replacement, $subject, $limit = -1 ) {
 577          $res = preg_replace(
 578              $this->getRegex(),
 579              StringUtils::escapeRegexReplacement( $replacement ),
 580              $subject,
 581              $limit
 582          );
 583          $this->mModified = $res !== $subject;
 584          return $res;
 585      }
 586  
 587      /**
 588       * Variable handling: {{SUBST:xxx}} style words
 589       * Calls back a function to determine what to replace xxx with
 590       * Input word must contain $1
 591       *
 592       * @param string $text
 593       * @param callable $callback
 594       *
 595       * @return string
 596       */
 597  	function substituteCallback( $text, $callback ) {
 598          $res = preg_replace_callback( $this->getVariableRegex(), $callback, $text );
 599          $this->mModified = $res !== $text;
 600          return $res;
 601      }
 602  
 603      /**
 604       * Matches the word, where $1 is a wildcard
 605       *
 606       * @return string
 607       */
 608  	function getVariableRegex() {
 609          if ( $this->mVariableRegex == '' ) {
 610              $this->initRegex();
 611          }
 612          return $this->mVariableRegex;
 613      }
 614  
 615      /**
 616       * Matches the entire string, where $1 is a wildcard
 617       *
 618       * @return string
 619       */
 620  	function getVariableStartToEndRegex() {
 621          if ( $this->mVariableStartToEndRegex == '' ) {
 622              $this->initRegex();
 623          }
 624          return $this->mVariableStartToEndRegex;
 625      }
 626  
 627      /**
 628       * Accesses the synonym list directly
 629       *
 630       * @param int $i
 631       *
 632       * @return string
 633       */
 634  	function getSynonym( $i ) {
 635          return $this->mSynonyms[$i];
 636      }
 637  
 638      /**
 639       * @return array
 640       */
 641  	function getSynonyms() {
 642          return $this->mSynonyms;
 643      }
 644  
 645      /**
 646       * Returns true if the last call to replace() or substituteCallback()
 647       * returned a modified text, otherwise false.
 648       *
 649       * @return bool
 650       */
 651  	function getWasModified() {
 652          return $this->mModified;
 653      }
 654  
 655      /**
 656       * $magicarr is an associative array of (magic word ID => replacement)
 657       * This method uses the php feature to do several replacements at the same time,
 658       * thereby gaining some efficiency. The result is placed in the out variable
 659       * $result. The return value is true if something was replaced.
 660       * @todo Should this be static? It doesn't seem to be used at all
 661       *
 662       * @param array $magicarr
 663       * @param string $subject
 664       * @param string $result
 665       *
 666       * @return bool
 667       */
 668  	function replaceMultiple( $magicarr, $subject, &$result ) {
 669          $search = array();
 670          $replace = array();
 671          foreach ( $magicarr as $id => $replacement ) {
 672              $mw = MagicWord::get( $id );
 673              $search[] = $mw->getRegex();
 674              $replace[] = $replacement;
 675          }
 676  
 677          $result = preg_replace( $search, $replace, $subject );
 678          return $result !== $subject;
 679      }
 680  
 681      /**
 682       * Adds all the synonyms of this MagicWord to an array, to allow quick
 683       * lookup in a list of magic words
 684       *
 685       * @param array $array
 686       * @param string $value
 687       */
 688  	function addToArray( &$array, $value ) {
 689          global $wgContLang;
 690          foreach ( $this->mSynonyms as $syn ) {
 691              $array[$wgContLang->lc( $syn )] = $value;
 692          }
 693      }
 694  
 695      /**
 696       * @return bool
 697       */
 698  	function isCaseSensitive() {
 699          return $this->mCaseSensitive;
 700      }
 701  
 702      /**
 703       * @return int
 704       */
 705  	function getId() {
 706          return $this->mId;
 707      }
 708  }
 709  
 710  /**
 711   * Class for handling an array of magic words
 712   * @ingroup Parser
 713   */
 714  class MagicWordArray {
 715      /** @var array */
 716      public $names = array();
 717  
 718      /** @var array */
 719      private $hash;
 720  
 721      private $baseRegex;
 722  
 723      private $regex;
 724  
 725      /** @todo Unused? */
 726      private $matches;
 727  
 728      /**
 729       * @param array $names
 730       */
 731  	function __construct( $names = array() ) {
 732          $this->names = $names;
 733      }
 734  
 735      /**
 736       * Add a magic word by name
 737       *
 738       * @param string $name
 739       */
 740  	public function add( $name ) {
 741          $this->names[] = $name;
 742          $this->hash = $this->baseRegex = $this->regex = null;
 743      }
 744  
 745      /**
 746       * Add a number of magic words by name
 747       *
 748       * @param array $names
 749       */
 750  	public function addArray( $names ) {
 751          $this->names = array_merge( $this->names, array_values( $names ) );
 752          $this->hash = $this->baseRegex = $this->regex = null;
 753      }
 754  
 755      /**
 756       * Get a 2-d hashtable for this array
 757       * @return array
 758       */
 759  	function getHash() {
 760          if ( is_null( $this->hash ) ) {
 761              global $wgContLang;
 762              $this->hash = array( 0 => array(), 1 => array() );
 763              foreach ( $this->names as $name ) {
 764                  $magic = MagicWord::get( $name );
 765                  $case = intval( $magic->isCaseSensitive() );
 766                  foreach ( $magic->getSynonyms() as $syn ) {
 767                      if ( !$case ) {
 768                          $syn = $wgContLang->lc( $syn );
 769                      }
 770                      $this->hash[$case][$syn] = $name;
 771                  }
 772              }
 773          }
 774          return $this->hash;
 775      }
 776  
 777      /**
 778       * Get the base regex
 779       * @return array
 780       */
 781  	function getBaseRegex() {
 782          if ( is_null( $this->baseRegex ) ) {
 783              $this->baseRegex = array( 0 => '', 1 => '' );
 784              foreach ( $this->names as $name ) {
 785                  $magic = MagicWord::get( $name );
 786                  $case = intval( $magic->isCaseSensitive() );
 787                  foreach ( $magic->getSynonyms() as $i => $syn ) {
 788                      // Group name must start with a non-digit in PCRE 8.34+
 789                      $it = strtr( $i, '0123456789', 'abcdefghij' );
 790                      $group = "(?P<{$it}_{$name}>" . preg_quote( $syn, '/' ) . ')';
 791                      if ( $this->baseRegex[$case] === '' ) {
 792                          $this->baseRegex[$case] = $group;
 793                      } else {
 794                          $this->baseRegex[$case] .= '|' . $group;
 795                      }
 796                  }
 797              }
 798          }
 799          return $this->baseRegex;
 800      }
 801  
 802      /**
 803       * Get an unanchored regex that does not match parameters
 804       * @return array
 805       */
 806  	function getRegex() {
 807          if ( is_null( $this->regex ) ) {
 808              $base = $this->getBaseRegex();
 809              $this->regex = array( '', '' );
 810              if ( $this->baseRegex[0] !== '' ) {
 811                  $this->regex[0] = "/{$base[0]}/iuS";
 812              }
 813              if ( $this->baseRegex[1] !== '' ) {
 814                  $this->regex[1] = "/{$base[1]}/S";
 815              }
 816          }
 817          return $this->regex;
 818      }
 819  
 820      /**
 821       * Get a regex for matching variables with parameters
 822       *
 823       * @return string
 824       */
 825  	function getVariableRegex() {
 826          return str_replace( "\\$1", "(.*?)", $this->getRegex() );
 827      }
 828  
 829      /**
 830       * Get a regex anchored to the start of the string that does not match parameters
 831       *
 832       * @return array
 833       */
 834  	function getRegexStart() {
 835          $base = $this->getBaseRegex();
 836          $newRegex = array( '', '' );
 837          if ( $base[0] !== '' ) {
 838              $newRegex[0] = "/^(?:{$base[0]})/iuS";
 839          }
 840          if ( $base[1] !== '' ) {
 841              $newRegex[1] = "/^(?:{$base[1]})/S";
 842          }
 843          return $newRegex;
 844      }
 845  
 846      /**
 847       * Get an anchored regex for matching variables with parameters
 848       *
 849       * @return array
 850       */
 851  	function getVariableStartToEndRegex() {
 852          $base = $this->getBaseRegex();
 853          $newRegex = array( '', '' );
 854          if ( $base[0] !== '' ) {
 855              $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
 856          }
 857          if ( $base[1] !== '' ) {
 858              $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
 859          }
 860          return $newRegex;
 861      }
 862  
 863      /**
 864       * @since 1.20
 865       * @return array
 866       */
 867  	public function getNames() {
 868          return $this->names;
 869      }
 870  
 871      /**
 872       * Parse a match array from preg_match
 873       * Returns array(magic word ID, parameter value)
 874       * If there is no parameter value, that element will be false.
 875       *
 876       * @param array $m
 877       *
 878       * @throws MWException
 879       * @return array
 880       */
 881  	function parseMatch( $m ) {
 882          reset( $m );
 883          while ( list( $key, $value ) = each( $m ) ) {
 884              if ( $key === 0 || $value === '' ) {
 885                  continue;
 886              }
 887              $parts = explode( '_', $key, 2 );
 888              if ( count( $parts ) != 2 ) {
 889                  // This shouldn't happen
 890                  // continue;
 891                  throw new MWException( __METHOD__ . ': bad parameter name' );
 892              }
 893              list( /* $synIndex */, $magicName ) = $parts;
 894              $paramValue = next( $m );
 895              return array( $magicName, $paramValue );
 896          }
 897          // This shouldn't happen either
 898          throw new MWException( __METHOD__ . ': parameter not found' );
 899      }
 900  
 901      /**
 902       * Match some text, with parameter capture
 903       * Returns an array with the magic word name in the first element and the
 904       * parameter in the second element.
 905       * Both elements are false if there was no match.
 906       *
 907       * @param string $text
 908       *
 909       * @return array
 910       */
 911  	public function matchVariableStartToEnd( $text ) {
 912          $regexes = $this->getVariableStartToEndRegex();
 913          foreach ( $regexes as $regex ) {
 914              if ( $regex !== '' ) {
 915                  $m = array();
 916                  if ( preg_match( $regex, $text, $m ) ) {
 917                      return $this->parseMatch( $m );
 918                  }
 919              }
 920          }
 921          return array( false, false );
 922      }
 923  
 924      /**
 925       * Match some text, without parameter capture
 926       * Returns the magic word name, or false if there was no capture
 927       *
 928       * @param string $text
 929       *
 930       * @return string|bool False on failure
 931       */
 932  	public function matchStartToEnd( $text ) {
 933          $hash = $this->getHash();
 934          if ( isset( $hash[1][$text] ) ) {
 935              return $hash[1][$text];
 936          }
 937          global $wgContLang;
 938          $lc = $wgContLang->lc( $text );
 939          if ( isset( $hash[0][$lc] ) ) {
 940              return $hash[0][$lc];
 941          }
 942          return false;
 943      }
 944  
 945      /**
 946       * Returns an associative array, ID => param value, for all items that match
 947       * Removes the matched items from the input string (passed by reference)
 948       *
 949       * @param string $text
 950       *
 951       * @return array
 952       */
 953  	public function matchAndRemove( &$text ) {
 954          $found = array();
 955          $regexes = $this->getRegex();
 956          foreach ( $regexes as $regex ) {
 957              if ( $regex === '' ) {
 958                  continue;
 959              }
 960              preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
 961              foreach ( $matches as $m ) {
 962                  list( $name, $param ) = $this->parseMatch( $m );
 963                  $found[$name] = $param;
 964              }
 965              $text = preg_replace( $regex, '', $text );
 966          }
 967          return $found;
 968      }
 969  
 970      /**
 971       * Return the ID of the magic word at the start of $text, and remove
 972       * the prefix from $text.
 973       * Return false if no match found and $text is not modified.
 974       * Does not match parameters.
 975       *
 976       * @param string $text
 977       *
 978       * @return int|bool False on failure
 979       */
 980  	public function matchStartAndRemove( &$text ) {
 981          $regexes = $this->getRegexStart();
 982          foreach ( $regexes as $regex ) {
 983              if ( $regex === '' ) {
 984                  continue;
 985              }
 986              if ( preg_match( $regex, $text, $m ) ) {
 987                  list( $id, ) = $this->parseMatch( $m );
 988                  if ( strlen( $m[0] ) >= strlen( $text ) ) {
 989                      $text = '';
 990                  } else {
 991                      $text = substr( $text, strlen( $m[0] ) );
 992                  }
 993                  return $id;
 994              }
 995          }
 996          return false;
 997      }
 998  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1