[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |