37 $wgHooks[
'ParserClearState'][] =
function () {
38 self::$mTimeChars = 0;
48 if ( !isset( self::$mExprParser ) ) {
63 return '<strong class="error">' . htmlspecialchars(
$e->getMessage() ) .
'</strong>';
74 public static function ifexpr(
$parser, $expr =
'', $then =
'', $else =
'' ) {
77 if ( is_numeric(
$ret ) ) {
86 return '<strong class="error">' . htmlspecialchars(
$e->getMessage() ) .
'</strong>';
97 $expr = isset(
$args[0] ) ? trim( $frame->expand(
$args[0] ) ) :
'';
98 $then =
$args[1] ??
'';
99 $else =
$args[2] ??
'';
114 $test = isset(
$args[0] ) ? trim( $frame->expand(
$args[0] ) ) :
'';
115 if ( $test !==
'' ) {
116 return isset(
$args[1] ) ? trim( $frame->expand(
$args[1] ) ) :
'';
118 return isset(
$args[2] ) ? trim( $frame->expand(
$args[2] ) ) :
'';
134 if ( $left == $right ) {
135 return isset(
$args[2] ) ? trim( $frame->expand(
$args[2] ) ) :
'';
137 return isset(
$args[3] ) ? trim( $frame->expand(
$args[3] ) ) :
'';
148 public static function iferror(
$parser, $test =
'', $then =
'', $else =
false ) {
150 '/<(?:strong|span|p|div)\s(?:[^\s>]*\s+)*?class="(?:[^"\s>]*\s+)*?error(?:\s[^">]*)?"/',
154 } elseif ( $else ===
false ) {
168 $test = isset(
$args[0] ) ? trim( $frame->expand(
$args[0] ) ) :
'';
169 $then =
$args[1] ??
false;
170 $else =
$args[2] ??
false;
175 return trim( $frame->expand(
$result ) );
190 $found = $defaultFound =
false;
192 $lastItemHadNoEquals =
false;
194 $mwDefault =
$parser->getMagicWordFactory()->get(
'default' );
196 $bits = $arg->splitArg();
197 $nameNode = $bits[
'name'];
198 $index = $bits[
'index'];
199 $valueNode = $bits[
'value'];
201 if ( $index ===
'' ) {
203 $lastItemHadNoEquals =
false;
205 # Multiple input match
206 return trim( $frame->expand( $valueNode ) );
210 if ( $test == $primary ) {
211 # Found a match, return now
212 return trim( $frame->expand( $valueNode ) );
213 } elseif ( $defaultFound || $mwDefault->matchStartToEnd( $test ) ) {
214 $default = $valueNode;
215 $defaultFound =
false;
216 } #
else wrong
case,
continue
219 # Multiple input, single output
220 # If the value matches, set a flag and continue
221 $lastItemHadNoEquals =
true;
225 if ( $decodedTest == $primary ) {
227 } elseif ( $mwDefault->matchStartToEnd( $decodedTest ) ) {
228 $defaultFound =
true;
233 # Check if the last item had no = sign, thus specifying the default case
234 if ( $lastItemHadNoEquals ) {
236 } elseif ( !is_null( $default ) ) {
237 return trim( $frame->expand( $default ) );
257 $from = trim( $from );
258 if ( $from ===
'' ) {
259 $from =
$parser->getTitle()->getPrefixedText();
262 $to = rtrim( $to,
' /' );
265 if ( $to ===
'' || $to ===
'.' ) {
270 if ( substr( $to, 0, 1 ) !==
'/' &&
271 substr( $to, 0, 2 ) !==
'./' &&
272 substr( $to, 0, 3 ) !==
'../' &&
278 $fullPath =
'/' . $from .
'/' . $to .
'/';
281 $fullPath = preg_replace(
'!/(\./)+!',
'/', $fullPath );
284 $fullPath = preg_replace(
'!/{2,}!',
'/', $fullPath );
287 $fullPath = trim( $fullPath,
'/' );
288 $exploded = explode(
'/', $fullPath );
291 foreach ( $exploded
as $current ) {
292 if ( $current ===
'..' ) {
293 if ( !
count( $newExploded ) ) {
295 $msg =
wfMessage(
'pfunc_rel2abs_invalid_depth', $fullPath )
296 ->inContentLanguage()->escaped();
297 return '<strong class="error">' . $msg .
'</strong>';
300 array_pop( $newExploded );
303 $newExploded[] = $current;
308 return implode(
'/', $newExploded );
321 $parser, $frame, $titletext =
'', $then =
'', $else =
''
324 $parser->getContentLanguage()->findVariantLink( $titletext,
$title,
true );
330 if ( !
$parser->incrementExpensiveFunctionCount() ) {
339 return $file->exists() ? $then : $else;
340 } elseif (
$title->isSpecialPage() ) {
346 ->exists(
$title->getDBkey() ) ? $then : $else;
347 } elseif (
$title->isExternal() ) {
354 $pdbk =
$title->getPrefixedDBkey();
356 $id = $lc->getGoodLinkID( $pdbk );
360 } elseif ( $lc->isBadLink( $pdbk ) ) {
364 if ( !
$parser->incrementExpensiveFunctionCount() ) {
367 $id =
$title->getArticleID();
387 $then =
$args[1] ??
null;
388 $else =
$args[2] ??
null;
394 return trim( $frame->expand(
$result ) );
408 $parser, $frame =
null, $format =
'', $date =
'', $language =
'', $local =
false
412 if ( $date ===
'' ) {
413 $cacheKey =
$parser->getOptions()->getTimestamp();
415 $date = $timestamp->getTimestamp( TS_ISO_8601 );
421 if ( isset( self::$mTimeCache[$format][$cacheKey][$language][$local] ) ) {
422 $cachedVal = self::$mTimeCache[$format][$cacheKey][$language][$local];
424 && $cachedVal[1] !==
null && $frame && is_callable( [ $frame,
'setTTL' ] )
426 $frame->setTTL( $cachedVal[1] );
428 return $cachedVal[0];
431 # compute the timestamp string $ts
432 # PHP >= 5.2 can handle dates before 1970 or after 2038 using the DateTime object
434 $invalidTime =
false;
436 # the DateTime constructor must be used because it throws exceptions
437 # when errors occur, whereas date_create appears to just output a warning
438 # that can't really be detected from within the code
441 # Default input timezone is UTC.
442 $utc =
new DateTimeZone(
'UTC' );
444 # Correct for DateTime interpreting 'XXXX' as XX:XX o'clock
445 if ( preg_match(
'/^[0-9]{4}$/', $date ) ) {
446 $date =
'00:00 ' . $date;
450 # UTC is a default input timezone.
451 $dateObject =
new DateTime( $date, $utc );
453 # Set output timezone.
458 $tz =
new DateTimeZone( date_default_timezone_get() );
463 $dateObject->setTimezone( $tz );
465 $ts = $dateObject->format(
'YmdHis' );
467 }
catch ( Exception $ex ) {
472 # format the timestamp and return the result
473 if ( $invalidTime ) {
474 $result =
'<strong class="error">' .
475 wfMessage(
'pfunc_time_error' )->inContentLanguage()->escaped() .
478 self::$mTimeChars += strlen( $format );
479 if ( self::$mTimeChars > self::MAX_TIME_CHARS ) {
480 return '<strong class="error">' .
481 wfMessage(
'pfunc_time_too_long' )->inContentLanguage()->escaped() .
485 return '<strong class="error">' .
486 wfMessage(
'pfunc_time_too_small' )->inContentLanguage()->escaped() .
488 } elseif ( $ts < 100000000000000 ) {
494 $langObject =
$parser->getFunctionLang();
498 $result = $langObject->sprintfDate( $format, $ts, $tz, $ttl );
500 return '<strong class="error">' .
501 wfMessage(
'pfunc_time_too_big' )->inContentLanguage()->escaped() .
506 self::$mTimeCache[$format][$cacheKey][$language][$local] = [
$result, $ttl ];
507 if ( $useTTL && $ttl !==
null && $frame && is_callable( [ $frame,
'setTTL' ] ) ) {
508 $frame->setTTL( $ttl );
522 $parser, $format =
'', $date =
'', $language =
'', $local =
false
534 $format = isset(
$args[0] ) ? trim( $frame->expand(
$args[0] ) ) :
'';
535 $date = isset(
$args[1] ) ? trim( $frame->expand(
$args[1] ) ) :
'';
536 $language = isset(
$args[2] ) ? trim( $frame->expand(
$args[2] ) ) :
'';
537 $local = isset(
$args[3] ) && trim( $frame->expand(
$args[3] ) );
559 $format = isset(
$args[0] ) ? trim( $frame->expand(
$args[0] ) ) :
'';
560 $date = isset(
$args[1] ) ? trim( $frame->expand(
$args[1] ) ) :
'';
561 $language = isset(
$args[2] ) ? trim( $frame->expand(
$args[2] ) ) :
'';
576 $parts = (int)$parts;
577 $offset = (int)$offset;
579 if ( $ntitle instanceof
Title ) {
580 $bits = explode(
'/', $ntitle->getPrefixedText(), 25 );
581 if (
count( $bits ) <= 0 ) {
582 return $ntitle->getPrefixedText();
587 if ( $parts === 0 ) {
588 return implode(
'/', array_slice( $bits, $offset ) );
590 return implode(
'/', array_slice( $bits, $offset, $parts ) );
604 global $wgPFStringLengthLimit;
605 return ( mb_strlen( $text ) < $wgPFStringLengthLimit );
613 global $wgPFStringLengthLimit;
614 $msg =
wfMessage(
'pfunc_string_too_long' )->numParams( $wgPFStringLengthLimit );
615 return '<strong class="error">' . $msg->inContentLanguage()->escaped() .
'</strong>';
627 $inStr =
$parser->killMarkers( (
string)$inStr );
628 return mb_strlen( $inStr );
644 public static function runPos(
$parser, $inStr =
'', $inNeedle =
'', $inOffset = 0 ) {
645 $inStr =
$parser->killMarkers( (
string)$inStr );
646 $inNeedle =
$parser->killMarkers( (
string)$inNeedle );
648 if ( !self::checkLength( $inStr ) ||
649 !self::checkLength( $inNeedle ) ) {
653 if ( $inNeedle ===
'' ) {
657 $pos = mb_strpos( $inStr, $inNeedle, min( (
int)$inOffset, mb_strlen( $inStr ) ) );
658 if ( $pos ===
false ) {
678 $inStr =
$parser->killMarkers( (
string)$inStr );
679 $inNeedle =
$parser->killMarkers( (
string)$inNeedle );
681 if ( !self::checkLength( $inStr ) ||
682 !self::checkLength( $inNeedle ) ) {
686 if ( $inNeedle ===
'' ) {
690 $pos = mb_strrpos( $inStr, $inNeedle );
691 if ( $pos ===
false ) {
716 public static function runSub(
$parser, $inStr =
'', $inStart = 0, $inLength = 0 ) {
717 $inStr =
$parser->killMarkers( (
string)$inStr );
719 if ( !self::checkLength( $inStr ) ) {
723 if ( (
int)$inLength === 0 ) {
724 $result = mb_substr( $inStr, (
int)$inStart );
726 $result = mb_substr( $inStr, (
int)$inStart, (
int)$inLength );
744 $inStr =
$parser->killMarkers( (
string)$inStr );
745 $inSubStr =
$parser->killMarkers( (
string)$inSubStr );
747 if ( !self::checkLength( $inStr ) ||
748 !self::checkLength( $inSubStr ) ) {
752 if ( $inSubStr ===
'' ) {
756 $result = mb_substr_count( $inStr, $inSubStr );
777 $inReplaceFrom =
'', $inReplaceTo =
'', $inLimit = -1 ) {
778 global $wgPFStringLengthLimit;
780 $inStr =
$parser->killMarkers( (
string)$inStr );
781 $inReplaceFrom =
$parser->killMarkers( (
string)$inReplaceFrom );
782 $inReplaceTo =
$parser->killMarkers( (
string)$inReplaceTo );
784 if ( !self::checkLength( $inStr ) ||
785 !self::checkLength( $inReplaceFrom ) ||
786 !self::checkLength( $inReplaceTo ) ) {
790 if ( $inReplaceFrom ===
'' ) {
791 $inReplaceFrom =
' ';
795 $diff = mb_strlen( $inReplaceTo ) - mb_strlen( $inReplaceFrom );
797 $limit = ( ( $wgPFStringLengthLimit - mb_strlen( $inStr ) ) / $diff ) + 1;
802 $inLimit = (int)$inLimit;
803 if ( $inLimit >= 0 ) {
804 if ( $limit > $inLimit || $limit == -1 ) {
810 $inReplaceFrom = preg_quote( $inReplaceFrom,
'/' );
813 $result = preg_replace(
'/' . $inReplaceFrom .
'/u',
814 $inReplaceTo, $inStr, $limit );
816 if ( !self::checkLength(
$result ) ) {
840 $parser, $inStr =
'', $inDiv =
'', $inPos = 0, $inLim =
null
842 $inStr =
$parser->killMarkers( (
string)$inStr );
843 $inDiv =
$parser->killMarkers( (
string)$inDiv );
845 if ( $inDiv ===
'' ) {
849 if ( !self::checkLength( $inStr ) ||
850 !self::checkLength( $inDiv ) ) {
854 $inDiv = preg_quote( $inDiv,
'/' );
856 $matches = preg_split(
'/' . $inDiv .
'/u', $inStr, $inLim );
858 if ( $inPos >= 0 && isset(
$matches[$inPos] ) ) {
878 $inStr =
$parser->killMarkers( (
string)$inStr );
879 if ( !self::checkLength( $inStr ) ) {
883 return urldecode( $inStr );
899 $expanded = $frame->expand( $obj );
900 $trimExpanded = trim( $expanded );
901 return trim( Sanitizer::decodeCharReferences( $expanded ) );