[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/extensions/ParserFunctions/ -> ParserFunctions_body.php (source)

   1  <?php
   2  
   3  class ExtParserFunctions {
   4      static $mExprParser;
   5      static $mTimeCache = array();
   6      static $mTimeChars = 0;
   7      static $mMaxTimeChars = 6000; # ~10 seconds
   8  
   9      /**
  10       * @param $parser Parser
  11       * @return bool
  12       */
  13  	public static function clearState( $parser ) {
  14          self::$mTimeChars = 0;
  15          $parser->pf_markerRegex = null;
  16          return true;
  17      }
  18  
  19      /**
  20       * Register ParserClearState hook.
  21       * We defer this until needed to avoid the loading of the code of this file
  22       * when no parser function is actually called.
  23       */
  24  	public static function registerClearHook() {
  25          static $done = false;
  26          if( !$done ) {
  27              global $wgHooks;
  28              $wgHooks['ParserClearState'][] = __CLASS__ . '::clearState';
  29              $done = true;
  30          }
  31      }
  32  
  33      /**
  34       * Get the marker regex. Cached.
  35       * @param $parser Parser
  36       * @return
  37       */
  38  	public static function getMarkerRegex( $parser ) {
  39          self::registerClearHook();
  40          if ( isset( $parser->pf_markerRegex ) ) {
  41              return $parser->pf_markerRegex;
  42          }
  43  
  44          wfProfileIn( __METHOD__ );
  45  
  46          $prefix = preg_quote( $parser->uniqPrefix(), '/' );
  47  
  48          $suffix = preg_quote( Parser::MARKER_SUFFIX, '/' );
  49  
  50          $parser->pf_markerRegex = '/' . $prefix . '(?:(?!' . $suffix . ').)*' . $suffix . '/us';
  51  
  52          wfProfileOut( __METHOD__ );
  53          return $parser->pf_markerRegex;
  54      }
  55  
  56      /**
  57       * @param $parser Parser
  58       * @param $text string
  59       * @return string
  60       */
  61  	private static function killMarkers ( $parser, $text ) {
  62          return preg_replace( self::getMarkerRegex( $parser ), '' , $text );
  63      }
  64  
  65      /**
  66       * @return ExprParser
  67       */
  68      public static function &getExprParser() {
  69          if ( !isset( self::$mExprParser ) ) {
  70              self::$mExprParser = new ExprParser;
  71          }
  72          return self::$mExprParser;
  73      }
  74  
  75      /**
  76       * @param $parser Parser
  77       * @param $expr string
  78       * @return string
  79       */
  80  	public static function expr( $parser, $expr = '' ) {
  81          try {
  82              return self::getExprParser()->doExpression( $expr );
  83          } catch ( ExprError $e ) {
  84              return '<strong class="error">' . htmlspecialchars( $e->getMessage() ) . '</strong>';
  85          }
  86      }
  87  
  88      /**
  89       * @param $parser Parser
  90       * @param $expr string
  91       * @param $then string
  92       * @param $else string
  93       * @return string
  94       */
  95  	public static function ifexpr( $parser, $expr = '', $then = '', $else = '' ) {
  96          try {
  97              $ret = self::getExprParser()->doExpression( $expr );
  98              if ( is_numeric( $ret ) ) {
  99                  $ret = floatval( $ret );
 100              }
 101              if ( $ret ) {
 102                  return $then;
 103              } else {
 104                  return $else;
 105              }
 106          } catch ( ExprError $e ) {
 107              return '<strong class="error">' . htmlspecialchars( $e->getMessage() ) . '</strong>';
 108          }
 109      }
 110  
 111      /**
 112       * @param $parser Parser
 113       * @param $frame PPFrame
 114       * @param $args array
 115       * @return string
 116       */
 117  	public static function ifexprObj( $parser, $frame, $args ) {
 118          $expr = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 119          $then = isset( $args[1] ) ? $args[1] : '';
 120          $else = isset( $args[2] ) ? $args[2] : '';
 121          $result = self::ifexpr( $parser, $expr, $then, $else );
 122          if ( is_object( $result ) ) {
 123              $result = trim( $frame->expand( $result ) );
 124          }
 125          return $result;
 126      }
 127  
 128      /**
 129       * @param $parser Parser
 130       * @param $frame PPFrame
 131       * @param $args array
 132       * @return string
 133       */
 134  	public static function ifObj( $parser, $frame, $args ) {
 135          $test = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 136          if ( $test !== '' ) {
 137              return isset( $args[1] ) ? trim( $frame->expand( $args[1] ) ) : '';
 138          } else {
 139              return isset( $args[2] ) ? trim( $frame->expand( $args[2] ) ) : '';
 140          }
 141      }
 142  
 143      /**
 144       * @param $parser Parser
 145       * @param $frame PPFrame
 146       * @param $args array
 147       * @return string
 148       */
 149  	public static function ifeqObj( $parser, $frame, $args ) {
 150          $left = isset( $args[0] ) ? self::decodeTrimExpand( $args[0], $frame ) : '';
 151          $right = isset( $args[1] ) ? self::decodeTrimExpand( $args[1], $frame ) : '';
 152          if ( $left == $right ) {
 153              return isset( $args[2] ) ? trim( $frame->expand( $args[2] ) ) : '';
 154          } else {
 155              return isset( $args[3] ) ? trim( $frame->expand( $args[3] ) ) : '';
 156          }
 157      }
 158  
 159      /**
 160       * @param $parser Parser
 161       * @param $test string
 162       * @param $then string
 163       * @param $else bool
 164       * @return bool|string
 165       */
 166  	public static function iferror( $parser, $test = '', $then = '', $else = false ) {
 167          if ( preg_match( '/<(?:strong|span|p|div)\s(?:[^\s>]*\s+)*?class="(?:[^"\s>]*\s+)*?error(?:\s[^">]*)?"/', $test ) ) {
 168              return $then;
 169          } elseif ( $else === false ) {
 170              return $test;
 171          } else {
 172              return $else;
 173          }
 174      }
 175  
 176      /**
 177       * @param $parser Parser
 178       * @param $frame PPFrame
 179       * @param $args array
 180       * @return string
 181       */
 182  	public static function iferrorObj( $parser, $frame, $args ) {
 183          $test = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 184          $then = isset( $args[1] ) ? $args[1] : false;
 185          $else = isset( $args[2] ) ? $args[2] : false;
 186          $result = self::iferror( $parser, $test, $then, $else );
 187          if ( $result === false ) {
 188              return '';
 189          } else {
 190              return trim( $frame->expand( $result ) );
 191          }
 192      }
 193  
 194      /**
 195       * @param $parser Parser
 196       * @param $frame PPFrame
 197       * @param $args
 198       * @return string
 199       */
 200  	public static function switchObj( $parser, $frame, $args ) {
 201          if ( count( $args ) == 0 ) {
 202              return '';
 203          }
 204          $primary = self::decodeTrimExpand( array_shift( $args ), $frame );
 205          $found = $defaultFound = false;
 206          $default = null;
 207          $lastItemHadNoEquals = false;
 208          $lastItem = '';
 209          $mwDefault =& MagicWord::get( 'default' );
 210          foreach ( $args as $arg ) {
 211              $bits = $arg->splitArg();
 212              $nameNode = $bits['name'];
 213              $index = $bits['index'];
 214              $valueNode = $bits['value'];
 215  
 216              if ( $index === '' ) {
 217                  # Found "="
 218                  $lastItemHadNoEquals = false;
 219                  if ( $found ) {
 220                      # Multiple input match
 221                      return trim( $frame->expand( $valueNode ) );
 222                  } else {
 223                      $test = self::decodeTrimExpand( $nameNode, $frame );
 224                      if ( $test == $primary ) {
 225                          # Found a match, return now
 226                          return trim( $frame->expand( $valueNode ) );
 227                      } elseif ( $defaultFound || $mwDefault->matchStartToEnd( $test ) ) {
 228                          $default = $valueNode;
 229                          $defaultFound = false;
 230                      } # else wrong case, continue
 231                  }
 232              } else {
 233                  # Multiple input, single output
 234                  # If the value matches, set a flag and continue
 235                  $lastItemHadNoEquals = true;
 236                  // $lastItem is an "out" variable
 237                  $decodedTest = self::decodeTrimExpand( $valueNode, $frame, $lastItem );
 238                  if ( $decodedTest == $primary ) {
 239                      $found = true;
 240                  } elseif ( $mwDefault->matchStartToEnd( $decodedTest ) ) {
 241                      $defaultFound = true;
 242                  }
 243              }
 244          }
 245          # Default case
 246          # Check if the last item had no = sign, thus specifying the default case
 247          if ( $lastItemHadNoEquals ) {
 248              return $lastItem;
 249          } elseif ( !is_null( $default ) ) {
 250              return trim( $frame->expand( $default ) );
 251          } else {
 252              return '';
 253          }
 254      }
 255  
 256      /**
 257       * Returns the absolute path to a subpage, relative to the current article
 258       * title. Treats titles as slash-separated paths.
 259       *
 260       * Following subpage link syntax instead of standard path syntax, an
 261       * initial slash is treated as a relative path, and vice versa.
 262       *
 263       * @param $parser Parser
 264       * @param $to string
 265       * @param $from string
 266       *
 267       * @return string
 268       */
 269  	public static function rel2abs( $parser , $to = '' , $from = '' ) {
 270  
 271          $from = trim( $from );
 272          if ( $from == '' ) {
 273              $from = $parser->getTitle()->getPrefixedText();
 274          }
 275  
 276          $to = rtrim( $to , ' /' );
 277  
 278          // if we have an empty path, or just one containing a dot
 279          if ( $to == '' || $to == '.' ) {
 280              return $from;
 281          }
 282  
 283          // if the path isn't relative
 284          if ( substr( $to , 0 , 1 ) != '/' &&
 285           substr( $to , 0 , 2 ) != './' &&
 286           substr( $to , 0 , 3 ) != '../' &&
 287           $to != '..' )
 288          {
 289              $from = '';
 290          }
 291          // Make a long path, containing both, enclose it in /.../
 292          $fullPath = '/' . $from . '/' .  $to . '/';
 293  
 294          // remove redundant current path dots
 295          $fullPath = preg_replace( '!/(\./)+!', '/', $fullPath );
 296  
 297          // remove double slashes
 298          $fullPath = preg_replace( '!/{2,}!', '/', $fullPath );
 299  
 300          // remove the enclosing slashes now
 301          $fullPath = trim( $fullPath , '/' );
 302          $exploded = explode ( '/' , $fullPath );
 303          $newExploded = array();
 304  
 305          foreach ( $exploded as $current ) {
 306              if ( $current == '..' ) { // removing one level
 307                  if ( !count( $newExploded ) ) {
 308                      // attempted to access a node above root node
 309                      $msg = wfMessage( 'pfunc_rel2abs_invalid_depth', $fullPath )->inContentLanguage()->escaped();
 310                      return '<strong class="error">' . $msg . '</strong>';
 311                  }
 312                  // remove last level from the stack
 313                  array_pop( $newExploded );
 314              } else {
 315                  // add the current level to the stack
 316                  $newExploded[] = $current;
 317              }
 318          }
 319  
 320          // we can now join it again
 321          return implode( '/' , $newExploded );
 322      }
 323  
 324      /**
 325       * @param $parser Parser
 326       * @param $frame PPFrame
 327       * @param $titletext string
 328       * @param $then string
 329       * @param $else string
 330       *
 331       * @return string
 332       */
 333  	public static function ifexistCommon( $parser, $frame, $titletext = '', $then = '', $else = '' ) {
 334          global $wgContLang;
 335          $title = Title::newFromText( $titletext );
 336          $wgContLang->findVariantLink( $titletext, $title, true );
 337          if ( $title ) {
 338              if ( $title->getNamespace() == NS_MEDIA ) {
 339                  /* If namespace is specified as NS_MEDIA, then we want to
 340                   * check the physical file, not the "description" page.
 341                   */
 342                  if ( !$parser->incrementExpensiveFunctionCount() ) {
 343                      return $else;
 344                  }
 345                  $file = wfFindFile( $title );
 346                  if ( !$file ) {
 347                      return $else;
 348                  }
 349                  $parser->mOutput->addImage(
 350                      $file->getName(), $file->getTimestamp(), $file->getSha1() );
 351                  return $file->exists() ? $then : $else;
 352              } elseif ( $title->getNamespace() == NS_SPECIAL ) {
 353                  /* Don't bother with the count for special pages,
 354                   * since their existence can be checked without
 355                   * accessing the database.
 356                   */
 357                  return SpecialPageFactory::exists( $title->getDBkey() ) ? $then : $else;
 358              } elseif ( $title->isExternal() ) {
 359                  /* Can't check the existence of pages on other sites,
 360                   * so just return $else.  Makes a sort of sense, since
 361                   * they don't exist _locally_.
 362                   */
 363                  return $else;
 364              } else {
 365                  $pdbk = $title->getPrefixedDBkey();
 366                  $lc = LinkCache::singleton();
 367                  $id = $lc->getGoodLinkID( $pdbk );
 368                  if ( $id != 0 ) {
 369                      $parser->mOutput->addLink( $title, $id );
 370                      return $then;
 371                  } elseif ( $lc->isBadLink( $pdbk ) ) {
 372                      $parser->mOutput->addLink( $title, 0 );
 373                      return $else;
 374                  }
 375                  if (  !$parser->incrementExpensiveFunctionCount() ) {
 376                      return $else;
 377                  }
 378                  $id = $title->getArticleID();
 379                  $parser->mOutput->addLink( $title, $id );
 380  
 381                  // bug 70495: don't just check whether the ID != 0
 382                  if ( $title->exists() ) {
 383                      return $then;
 384                  }
 385              }
 386          }
 387          return $else;
 388      }
 389  
 390      /**
 391       * @param $parser Parser
 392       * @param $frame PPFrame
 393       * @param $args array
 394       * @return string
 395       */
 396  	public static function ifexistObj( $parser, $frame, $args ) {
 397          $title = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 398          $then = isset( $args[1] ) ? $args[1] : null;
 399          $else = isset( $args[2] ) ? $args[2] : null;
 400  
 401          $result = self::ifexistCommon( $parser, $frame, $title, $then, $else );
 402          if ( $result === null ) {
 403              return '';
 404          } else {
 405              return trim( $frame->expand( $result ) );
 406          }
 407      }
 408  
 409      /**
 410       * @param $parser Parser
 411       * @param $frame PPFrame
 412       * @param $format string
 413       * @param $date string
 414       * @param $language string
 415       * @param $local string|bool
 416       * @return string
 417       */
 418  	public static function timeCommon( $parser, $frame = null, $format = '', $date = '', $language = '', $local = false ) {
 419          global $wgLocaltimezone;
 420          self::registerClearHook();
 421          if ( $date === '' ) {
 422              $cacheKey = $parser->getOptions()->getTimestamp();
 423              $timestamp = new MWTimestamp( $cacheKey );
 424              $date = $timestamp->getTimestamp( TS_ISO_8601 );
 425              $useTTL = true;
 426          } else {
 427              $cacheKey = $date;
 428              $useTTL = false;
 429          }
 430          if ( isset( self::$mTimeCache[$format][$cacheKey][$language][$local] ) ) {
 431              $cachedVal = self::$mTimeCache[$format][$cacheKey][$language][$local];
 432              if ( $useTTL && $cachedVal[1] !== null && $frame && is_callable( array( $frame, 'setTTL' ) ) ) {
 433                  $frame->setTTL( $cachedVal[1] );
 434              }
 435              return $cachedVal[0];
 436          }
 437  
 438          # compute the timestamp string $ts
 439          # PHP >= 5.2 can handle dates before 1970 or after 2038 using the DateTime object
 440  
 441          $invalidTime = false;
 442  
 443          # the DateTime constructor must be used because it throws exceptions
 444          # when errors occur, whereas date_create appears to just output a warning
 445          # that can't really be detected from within the code
 446          try {
 447  
 448              # Default input timezone is UTC.
 449              $utc = new DateTimeZone( 'UTC' );
 450  
 451              # Correct for DateTime interpreting 'XXXX' as XX:XX o'clock
 452              if ( preg_match( '/^[0-9]{4}$/', $date ) ) {
 453                  $date = '00:00 '.$date;
 454              }
 455  
 456              # Parse date
 457              # UTC is a default input timezone.
 458              $dateObject = new DateTime( $date, $utc );
 459  
 460              # Set output timezone.
 461              if ( $local ) {
 462                  if ( isset( $wgLocaltimezone ) ) {
 463                      $tz = new DateTimeZone( $wgLocaltimezone );
 464                  } else {
 465                      $tz = new DateTimeZone( date_default_timezone_get() );
 466                  }
 467              } else {
 468                  $tz = $utc;
 469              }
 470              $dateObject->setTimezone( $tz );
 471              # Generate timestamp
 472              $ts = $dateObject->format( 'YmdHis' );
 473  
 474          } catch ( Exception $ex ) {
 475              $invalidTime = true;
 476          }
 477  
 478          $ttl = null;
 479          # format the timestamp and return the result
 480          if ( $invalidTime ) {
 481              $result = '<strong class="error">' . wfMessage( 'pfunc_time_error' )->inContentLanguage()->escaped() . '</strong>';
 482          } else {
 483              self::$mTimeChars += strlen( $format );
 484              if ( self::$mTimeChars > self::$mMaxTimeChars ) {
 485                  return '<strong class="error">' . wfMessage( 'pfunc_time_too_long' )->inContentLanguage()->escaped() . '</strong>';
 486              } else {
 487                  if ( $ts < 0 ) { // Language can't deal with BC years
 488                      return '<strong class="error">' . wfMessage( 'pfunc_time_too_small' )->inContentLanguage()->escaped() . '</strong>';
 489                  } elseif ( $ts < 100000000000000 ) { // Language can't deal with years after 9999
 490                      if ( $language !== '' && Language::isValidBuiltInCode( $language ) ) {
 491                          // use whatever language is passed as a parameter
 492                          $langObject = Language::factory( $language );
 493                      } else {
 494                          // use wiki's content language
 495                          $langObject = $parser->getFunctionLang();
 496                          StubObject::unstub( $langObject ); // $ttl is passed by reference, which doesn't work right on stub objects
 497                      }
 498                      $result = $langObject->sprintfDate( $format, $ts, $tz, $ttl );
 499                  } else {
 500                      return '<strong class="error">' . wfMessage( 'pfunc_time_too_big' )->inContentLanguage()->escaped() . '</strong>';
 501                  }
 502              }
 503          }
 504          self::$mTimeCache[$format][$cacheKey][$language][$local] = array( $result, $ttl );
 505          if ( $useTTL && $ttl !== null && $frame && is_callable( array( $frame, 'setTTL' ) ) ) {
 506              $frame->setTTL( $ttl );
 507          }
 508          return $result;
 509      }
 510  
 511      /**
 512       * @param $parser Parser
 513       * @param $format string
 514       * @param $date string
 515       * @param $language string
 516       * @param $local string|bool
 517       * @return string
 518       */
 519  	public static function time( $parser, $format = '', $date = '', $language = '', $local = false ) {
 520          return self::timeCommon( $parser, null, $format, $date, $language, $local );
 521      }
 522  
 523  
 524      /**
 525       * @param $parser Parser
 526       * @param $frame PPFrame
 527       * @param $args array
 528       * @return string
 529       */
 530  	public static function timeObj( $parser, $frame, $args ) {
 531          $format = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 532          $date = isset( $args[1] ) ? trim( $frame->expand( $args[1] ) ) : '';
 533          $language = isset( $args[2] ) ? trim( $frame->expand( $args[2] ) ) : '';
 534          $local = isset( $args[3] ) && trim( $frame->expand( $args[3] ) );
 535          return self::timeCommon( $parser, $frame, $format, $date, $language, $local );
 536      }
 537  
 538      /**
 539       * @param $parser Parser
 540       * @param $format string
 541       * @param $date string
 542       * @param $language string
 543       * @return string
 544       */
 545  	public static function localTime( $parser, $format = '', $date = '', $language = '' ) {
 546          return self::timeCommon( $parser, null, $format, $date, $language, true );
 547      }
 548  
 549      /**
 550       * @param $parser Parser
 551       * @param $frame PPFrame
 552       * @param $args array
 553       * @return string
 554       */
 555  	public static function localTimeObj( $parser, $frame, $args ) {
 556          $format = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
 557          $date = isset( $args[1] ) ? trim( $frame->expand( $args[1] ) ) : '';
 558          $language = isset( $args[2] ) ? trim( $frame->expand( $args[2] ) ) : '';
 559          return self::timeCommon( $parser, $frame, $format, $date, $language, true );
 560      }
 561  
 562      /**
 563       * Obtain a specified number of slash-separated parts of a title,
 564       * e.g. {{#titleparts:Hello/World|1}} => "Hello"
 565       *
 566       * @param $parser Parser Parent parser
 567       * @param $title string Title to split
 568       * @param $parts int Number of parts to keep
 569       * @param $offset int Offset starting at 1
 570       * @return string
 571       */
 572  	public static function titleparts( $parser, $title = '', $parts = 0, $offset = 0 ) {
 573          $parts = intval( $parts );
 574          $offset = intval( $offset );
 575          $ntitle = Title::newFromText( $title );
 576          if ( $ntitle instanceof Title ) {
 577              $bits = explode( '/', $ntitle->getPrefixedText(), 25 );
 578              if ( count( $bits ) <= 0 ) {
 579                   return $ntitle->getPrefixedText();
 580              } else {
 581                  if ( $offset > 0 ) {
 582                      --$offset;
 583                  }
 584                  if ( $parts == 0 ) {
 585                      return implode( '/', array_slice( $bits, $offset ) );
 586                  } else {
 587                      return implode( '/', array_slice( $bits, $offset, $parts ) );
 588                  }
 589              }
 590          } else {
 591              return $title;
 592          }
 593      }
 594  
 595      /**
 596       *  Verifies parameter is less than max string length.
 597       * @param $text
 598       * @return bool
 599       */
 600  	private static function checkLength( $text ) {
 601          global $wgPFStringLengthLimit;
 602          return ( mb_strlen( $text ) < $wgPFStringLengthLimit );
 603      }
 604  
 605      /**
 606       * Generates error message.  Called when string is too long.
 607       * @return string
 608       */
 609  	private static function tooLongError() {
 610          global $wgPFStringLengthLimit;
 611          $msg = wfMessage( 'pfunc_string_too_long' )->numParams( $wgPFStringLengthLimit );
 612          return '<strong class="error">' . $msg->inContentLanguage()->escaped() . '</strong>';
 613      }
 614  
 615      /**
 616       * {{#len:string}}
 617       *
 618       * Reports number of characters in string.
 619       * @param $parser Parser
 620       * @param $inStr string
 621       * @return int
 622       */
 623  	public static function runLen ( $parser, $inStr = '' ) {
 624          wfProfileIn( __METHOD__ );
 625  
 626          $inStr = self::killMarkers( $parser, (string)$inStr );
 627          $len = mb_strlen( $inStr );
 628  
 629          wfProfileOut( __METHOD__ );
 630          return $len;
 631      }
 632  
 633      /**
 634       * {{#pos: string | needle | offset}}
 635       *
 636       * Finds first occurrence of "needle" in "string" starting at "offset".
 637       *
 638       * Note: If the needle is an empty string, single space is used instead.
 639       * Note: If the needle is not found, empty string is returned.
 640       * @param $parser Parser
 641       * @param $inStr string
 642       * @param $inNeedle int|string
 643       * @param $inOffset int
 644       * @return int|string
 645       */
 646  	public static function runPos ( $parser, $inStr = '', $inNeedle = '', $inOffset = 0 ) {
 647          wfProfileIn( __METHOD__ );
 648  
 649          $inStr = self::killMarkers( $parser, (string)$inStr );
 650          $inNeedle = self::killMarkers( $parser, (string)$inNeedle );
 651  
 652          if ( !self::checkLength( $inStr ) ||
 653              !self::checkLength( $inNeedle ) ) {
 654              wfProfileOut( __METHOD__ );
 655              return self::tooLongError();
 656          }
 657  
 658          if ( $inNeedle == '' ) { $inNeedle = ' '; }
 659  
 660          $pos = mb_strpos( $inStr, $inNeedle, $inOffset );
 661          if ( $pos === false ) { $pos = ""; }
 662  
 663          wfProfileOut( __METHOD__ );
 664          return $pos;
 665      }
 666  
 667      /**
 668       * {{#rpos: string | needle}}
 669       *
 670       * Finds last occurrence of "needle" in "string".
 671       *
 672       * Note: If the needle is an empty string, single space is used instead.
 673       * Note: If the needle is not found, -1 is returned.
 674       * @param $parser Parser
 675       * @param $inStr string
 676       * @param $inNeedle int|string
 677       * @return int|string
 678       */
 679  	public static function runRPos ( $parser, $inStr = '', $inNeedle = '' ) {
 680          wfProfileIn( __METHOD__ );
 681  
 682          $inStr = self::killMarkers( $parser, (string)$inStr );
 683          $inNeedle = self::killMarkers( $parser, (string)$inNeedle );
 684  
 685          if ( !self::checkLength( $inStr ) ||
 686              !self::checkLength( $inNeedle ) ) {
 687              wfProfileOut( __METHOD__ );
 688              return self::tooLongError();
 689          }
 690  
 691          if ( $inNeedle == '' ) { $inNeedle = ' '; }
 692  
 693          $pos = mb_strrpos( $inStr, $inNeedle );
 694          if ( $pos === false ) { $pos = -1; }
 695  
 696          wfProfileOut( __METHOD__ );
 697          return $pos;
 698      }
 699  
 700      /**
 701       * {{#sub: string | start | length }}
 702       *
 703       * Returns substring of "string" starting at "start" and having
 704       * "length" characters.
 705       *
 706       * Note: If length is zero, the rest of the input is returned.
 707       * Note: A negative value for "start" operates from the end of the
 708       *   "string".
 709       * Note: A negative value for "length" returns a string reduced in
 710       *   length by that amount.
 711       *
 712       * @param $parser Parser
 713       * @param $inStr string
 714       * @param $inStart int
 715       * @param $inLength int
 716       * @return string
 717       */
 718  	public static function runSub ( $parser, $inStr = '', $inStart = 0, $inLength = 0 ) {
 719          wfProfileIn( __METHOD__ );
 720  
 721          $inStr = self::killMarkers( $parser, (string)$inStr );
 722  
 723          if ( !self::checkLength( $inStr ) ) {
 724              wfProfileOut( __METHOD__ );
 725              return self::tooLongError();
 726          }
 727  
 728          if ( intval( $inLength ) == 0 ) {
 729              $result = mb_substr( $inStr, intval( $inStart ) );
 730          } else {
 731              $result = mb_substr( $inStr, intval( $inStart ), intval( $inLength ) );
 732          }
 733  
 734          wfProfileOut( __METHOD__ );
 735          return $result;
 736      }
 737  
 738      /**
 739       * {{#count: string | substr }}
 740       *
 741       * Returns number of occurrences of "substr" in "string".
 742       *
 743       * Note: If "substr" is empty, a single space is used.
 744       * @param $parser
 745       * @param $inStr string
 746       * @param $inSubStr string
 747       * @return int|string
 748       */
 749  	public static function runCount ( $parser, $inStr = '', $inSubStr = '' ) {
 750          wfProfileIn( __METHOD__ );
 751  
 752          $inStr = self::killMarkers( $parser, (string)$inStr );
 753          $inSubStr = self::killMarkers( $parser, (string)$inSubStr );
 754  
 755          if ( !self::checkLength( $inStr ) ||
 756              !self::checkLength( $inSubStr ) ) {
 757              wfProfileOut( __METHOD__ );
 758              return self::tooLongError();
 759          }
 760  
 761          if ( $inSubStr == '' ) {
 762              $inSubStr = ' ';
 763          }
 764  
 765          $result = mb_substr_count( $inStr, $inSubStr );
 766  
 767          wfProfileOut( __METHOD__ );
 768          return $result;
 769      }
 770  
 771      /**
 772       * {{#replace:string | from | to | limit }}
 773       *
 774       * Replaces each occurrence of "from" in "string" with "to".
 775       * At most "limit" replacements are performed.
 776       *
 777       * Note: Armored against replacements that would generate huge strings.
 778       * Note: If "from" is an empty string, single space is used instead.
 779       * @param $parser Parser
 780       * @param $inStr string
 781       * @param $inReplaceFrom string
 782       * @param $inReplaceTo string
 783       * @param $inLimit int
 784       * @return mixed|string
 785       */
 786  	public static function runReplace( $parser, $inStr = '',
 787              $inReplaceFrom = '', $inReplaceTo = '', $inLimit = -1 ) {
 788          global $wgPFStringLengthLimit;
 789          wfProfileIn( __METHOD__ );
 790  
 791          $inStr = self::killMarkers( $parser, (string)$inStr );
 792          $inReplaceFrom = self::killMarkers( $parser, (string)$inReplaceFrom );
 793          $inReplaceTo = self::killMarkers( $parser, (string)$inReplaceTo );
 794  
 795          if ( !self::checkLength( $inStr ) ||
 796              !self::checkLength( $inReplaceFrom ) ||
 797              !self::checkLength( $inReplaceTo ) ) {
 798              wfProfileOut( __METHOD__ );
 799              return self::tooLongError();
 800          }
 801  
 802          if ( $inReplaceFrom == '' ) { $inReplaceFrom = ' '; }
 803  
 804          // Precompute limit to avoid generating enormous string:
 805          $diff = mb_strlen( $inReplaceTo ) - mb_strlen( $inReplaceFrom );
 806          if ( $diff > 0 ) {
 807              $limit = ( ( $wgPFStringLengthLimit - mb_strlen( $inStr ) ) / $diff ) + 1;
 808          } else {
 809              $limit = -1;
 810          }
 811  
 812          $inLimit = intval( $inLimit );
 813          if ( $inLimit >= 0 ) {
 814              if ( $limit > $inLimit || $limit == -1 ) { $limit = $inLimit; }
 815          }
 816  
 817          // Use regex to allow limit and handle UTF-8 correctly.
 818          $inReplaceFrom = preg_quote( $inReplaceFrom, '/' );
 819          $inReplaceTo = StringUtils::escapeRegexReplacement( $inReplaceTo );
 820  
 821          $result = preg_replace( '/' . $inReplaceFrom . '/u',
 822                          $inReplaceTo, $inStr, $limit );
 823  
 824          if ( !self::checkLength( $result ) ) {
 825              wfProfileOut( __METHOD__ );
 826              return self::tooLongError();
 827          }
 828  
 829          wfProfileOut( __METHOD__ );
 830          return $result;
 831      }
 832  
 833  
 834      /**
 835       * {{#explode:string | delimiter | position | limit}}
 836       *
 837       * Breaks "string" into chunks separated by "delimiter" and returns the
 838       * chunk identified by "position".
 839       *
 840       * Note: Negative position can be used to specify tokens from the end.
 841       * Note: If the divider is an empty string, single space is used instead.
 842       * Note: Empty string is returned if there are not enough exploded chunks.
 843       * @param $parser Parser
 844       * @param $inStr string
 845       * @param $inDiv string
 846       * @param $inPos int
 847       * @param $inLim int|null
 848       * @return string
 849       */
 850  	public static function runExplode ( $parser, $inStr = '', $inDiv = '', $inPos = 0, $inLim = null ) {
 851          wfProfileIn( __METHOD__ );
 852  
 853          $inStr = self::killMarkers( $parser, (string)$inStr );
 854          $inDiv = self::killMarkers( $parser, (string)$inDiv );
 855  
 856          if ( $inDiv == '' ) {
 857              $inDiv = ' ';
 858          }
 859  
 860          if ( !self::checkLength( $inStr ) ||
 861              !self::checkLength( $inDiv ) ) {
 862              wfProfileOut( __METHOD__ );
 863              return self::tooLongError();
 864          }
 865  
 866          $inDiv = preg_quote( $inDiv, '/' );
 867  
 868          $matches = preg_split( '/' . $inDiv . '/u', $inStr, $inLim );
 869  
 870          if ( $inPos >= 0 && isset( $matches[$inPos] ) ) {
 871              $result = $matches[$inPos];
 872          } elseif ( $inPos < 0 && isset( $matches[count( $matches ) + $inPos] ) ) {
 873              $result = $matches[count( $matches ) + $inPos];
 874          } else {
 875              $result = '';
 876          }
 877  
 878          wfProfileOut( __METHOD__ );
 879          return $result;
 880      }
 881  
 882      /**
 883       * {{#urldecode:string}}
 884       *
 885       * Decodes URL-encoded (like%20that) strings.
 886       * @param $parser Parser
 887       * @param $inStr string
 888       * @return string
 889       */
 890  	public static function runUrlDecode( $parser, $inStr = '' ) {
 891          wfProfileIn( __METHOD__ );
 892  
 893          $inStr = self::killMarkers( $parser, (string)$inStr );
 894          if ( !self::checkLength( $inStr ) ) {
 895              wfProfileOut( __METHOD__ );
 896              return self::tooLongError();
 897          }
 898  
 899          $result = urldecode( $inStr );
 900  
 901          wfProfileOut( __METHOD__ );
 902          return $result;
 903      }
 904  
 905      /**
 906       * Take a PPNode (-ish thing), expand it, remove entities, and trim.
 907       *
 908       * For use when doing string comparisions, where user expects entities
 909       * to be equal for what they stand for (e.g. comparisions with {{PAGENAME}})
 910       *
 911       * @param $obj PPNode|string Thing to expand
 912       * @param $frame PPFrame
 913       * @param &$trimExpanded String Expanded and trimmed version of PPNode, but with char refs intact
 914       * @return String The trimmed, expanded and entity reference decoded version of the PPNode
 915       */
 916  	private static function decodeTrimExpand( $obj, $frame, &$trimExpanded = null ) {
 917          $expanded = $frame->expand( $obj );
 918          $trimExpanded = trim( $expanded );
 919          return trim( Sanitizer::decodeCharReferences( $expanded ) );
 920      }
 921  }


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