MediaWiki  master
CoreMagicVariables.php
Go to the documentation of this file.
1 <?php
26 use Psr\Log\LoggerInterface;
27 use Wikimedia\Timestamp\ConvertibleTimestamp;
28 
36  private const MAX_TTS = 900;
37 
51  public static function expand(
52  // Fundamental options
53  Parser $parser,
54  string $id,
55  // Context passed over from the parser
56  ConvertibleTimestamp $ts,
57  NamespaceInfo $nsInfo,
58  ServiceOptions $svcOptions,
59  LoggerInterface $logger
60  ): ?string {
61  $pageLang = $parser->getFunctionLang();
62  $title = $parser->getTitle();
63 
64  switch ( $id ) {
65  case '!':
66  return '|';
67  case '=':
68  return '=';
69  case 'currentmonth':
70  return $pageLang->formatNumNoSeparators( $ts->format( 'm' ) );
71  case 'currentmonth1':
72  return $pageLang->formatNumNoSeparators( $ts->format( 'n' ) );
73  case 'currentmonthname':
74  return $pageLang->getMonthName( (int)$ts->format( 'n' ) );
75  case 'currentmonthnamegen':
76  return $pageLang->getMonthNameGen( (int)$ts->format( 'n' ) );
77  case 'currentmonthabbrev':
78  return $pageLang->getMonthAbbreviation( (int)$ts->format( 'n' ) );
79  case 'currentday':
80  return $pageLang->formatNumNoSeparators( $ts->format( 'j' ) );
81  case 'currentday2':
82  return $pageLang->formatNumNoSeparators( $ts->format( 'd' ) );
83  case 'localmonth':
84  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'm' ) );
85  case 'localmonth1':
86  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'n' ) );
87  case 'localmonthname':
88  return $pageLang->getMonthName( (int)self::makeTsLocal( $svcOptions, $ts )->format( 'n' ) );
89  case 'localmonthnamegen':
90  return $pageLang->getMonthNameGen( (int)self::makeTsLocal( $svcOptions, $ts )->format( 'n' ) );
91  case 'localmonthabbrev':
92  return $pageLang->getMonthAbbreviation( (int)self::makeTsLocal( $svcOptions, $ts )->format( 'n' ) );
93  case 'localday':
94  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'j' ) );
95  case 'localday2':
96  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'd' ) );
97  case 'pagename':
98  return wfEscapeWikiText( $title->getText() );
99  case 'pagenamee':
100  return wfEscapeWikiText( $title->getPartialURL() );
101  case 'fullpagename':
102  return wfEscapeWikiText( $title->getPrefixedText() );
103  case 'fullpagenamee':
104  return wfEscapeWikiText( $title->getPrefixedURL() );
105  case 'subpagename':
106  return wfEscapeWikiText( $title->getSubpageText() );
107  case 'subpagenamee':
108  return wfEscapeWikiText( $title->getSubpageUrlForm() );
109  case 'rootpagename':
110  return wfEscapeWikiText( $title->getRootText() );
111  case 'rootpagenamee':
112  return wfEscapeWikiText( wfUrlencode( str_replace(
113  ' ',
114  '_',
115  $title->getRootText()
116  ) ) );
117  case 'basepagename':
118  return wfEscapeWikiText( $title->getBaseText() );
119  case 'basepagenamee':
120  return wfEscapeWikiText( wfUrlencode( str_replace(
121  ' ',
122  '_',
123  $title->getBaseText()
124  ) ) );
125  case 'talkpagename':
126  if ( $title->canHaveTalkPage() ) {
127  $talkPage = $title->getTalkPage();
128  return wfEscapeWikiText( $talkPage->getPrefixedText() );
129  }
130  return '';
131  case 'talkpagenamee':
132  if ( $title->canHaveTalkPage() ) {
133  $talkPage = $title->getTalkPage();
134  return wfEscapeWikiText( $talkPage->getPrefixedURL() );
135  }
136  return '';
137  case 'subjectpagename':
138  $subjPage = $title->getSubjectPage();
139  return wfEscapeWikiText( $subjPage->getPrefixedText() );
140  case 'subjectpagenamee':
141  $subjPage = $title->getSubjectPage();
142  return wfEscapeWikiText( $subjPage->getPrefixedURL() );
143  case 'pageid': // requested in T25427
144  // Inform the edit saving system that getting the canonical output
145  // after page insertion requires a parse that used that exact page ID
146  self::setOutputFlag( $parser, $logger, ParserOutputFlags::VARY_PAGE_ID, '{{PAGEID}} used' );
147  $value = $title->getArticleID();
148  if ( !$value ) {
149  $value = $parser->getOptions()->getSpeculativePageId();
150  if ( $value ) {
151  $parser->getOutput()->setSpeculativePageIdUsed( $value );
152  }
153  }
154  return (string)$value;
155  case 'revisionid':
156  $namespace = $title->getNamespace();
157  if (
158  $svcOptions->get( MainConfigNames::MiserMode ) &&
159  !$parser->getOptions()->getInterfaceMessage() &&
160  // @TODO: disallow this variable on all namespaces
161  $nsInfo->isSubject( $namespace )
162  ) {
163  // Use a stub result instead of the actual revision ID in order to avoid
164  // double parses on page save but still allow preview detection (T137900)
165  if ( $parser->getRevisionId() || $parser->getOptions()->getSpeculativeRevId() ) {
166  return '-';
167  } else {
168  self::setOutputFlag(
169  $parser,
170  $logger,
171  ParserOutputFlags::VARY_REVISION_EXISTS,
172  '{{REVISIONID}} used'
173  );
174  return '';
175  }
176  } else {
177  // Inform the edit saving system that getting the canonical output after
178  // revision insertion requires a parse that used that exact revision ID
179  self::setOutputFlag( $parser, $logger, ParserOutputFlags::VARY_REVISION_ID, '{{REVISIONID}} used' );
180  $value = $parser->getRevisionId();
181  if ( $value === 0 ) {
182  $rev = $parser->getRevisionRecordObject();
183  $value = $rev ? $rev->getId() : $value;
184  }
185  if ( !$value ) {
186  $value = $parser->getOptions()->getSpeculativeRevId();
187  if ( $value ) {
188  $parser->getOutput()->setSpeculativeRevIdUsed( $value );
189  }
190  }
191  return (string)$value;
192  }
193  case 'revisionday':
194  return strval( (int)self::getRevisionTimestampSubstring(
195  $parser, $logger, 6, 2, self::MAX_TTS, $id
196  ) );
197  case 'revisionday2':
198  return self::getRevisionTimestampSubstring(
199  $parser, $logger, 6, 2, self::MAX_TTS, $id
200  );
201  case 'revisionmonth':
202  return self::getRevisionTimestampSubstring(
203  $parser, $logger, 4, 2, self::MAX_TTS, $id
204  );
205  case 'revisionmonth1':
206  return strval( (int)self::getRevisionTimestampSubstring(
207  $parser, $logger, 4, 2, self::MAX_TTS, $id
208  ) );
209  case 'revisionyear':
210  return self::getRevisionTimestampSubstring(
211  $parser, $logger, 0, 4, self::MAX_TTS, $id
212  );
213  case 'revisiontimestamp':
214  return self::getRevisionTimestampSubstring(
215  $parser, $logger, 0, 14, self::MAX_TTS, $id
216  );
217  case 'revisionuser':
218  // Inform the edit saving system that getting the canonical output after
219  // revision insertion requires a parse that used the actual user ID
220  self::setOutputFlag( $parser, $logger, ParserOutputFlags::VARY_USER, '{{REVISIONUSER}} used' );
221  // Note that getRevisionUser() can return null; we need to
222  // be sure to cast this to (an empty) string, since 'null'
223  // means "magic variable not handled here".
224  return (string)$parser->getRevisionUser();
225  case 'revisionsize':
226  return (string)$parser->getRevisionSize();
227  case 'namespace':
228  return str_replace( '_', ' ',
229  $parser->getContentLanguage()->getNsText( $title->getNamespace() ) );
230  case 'namespacee':
231  return wfUrlencode( $parser->getContentLanguage()->getNsText( $title->getNamespace() ) );
232  case 'namespacenumber':
233  return (string)$title->getNamespace();
234  case 'talkspace':
235  return $title->canHaveTalkPage()
236  ? str_replace( '_', ' ', $title->getTalkNsText() )
237  : '';
238  case 'talkspacee':
239  return $title->canHaveTalkPage()
240  ? wfUrlencode( $title->getTalkNsText() )
241  : '';
242  case 'subjectspace':
243  return str_replace( '_', ' ', $title->getSubjectNsText() );
244  case 'subjectspacee':
245  return ( wfUrlencode( $title->getSubjectNsText() ) );
246  case 'currentdayname':
247  return $pageLang->getWeekdayName( (int)$ts->format( 'w' ) + 1 );
248  case 'currentyear':
249  return $pageLang->formatNumNoSeparators( $ts->format( 'Y' ) );
250  case 'currenttime':
251  return $pageLang->time( $ts->getTimestamp( TS_MW ), false, false );
252  case 'currenthour':
253  return $pageLang->formatNumNoSeparators( $ts->format( 'H' ) );
254  case 'currentweek':
255  // @bug T6594 PHP5 has it zero padded, PHP4 does not, cast to
256  // int to remove the padding
257  return $pageLang->formatNum( (int)$ts->format( 'W' ) );
258  case 'currentdow':
259  return $pageLang->formatNum( $ts->format( 'w' ) );
260  case 'localdayname':
261  return $pageLang->getWeekdayName(
262  (int)self::makeTsLocal( $svcOptions, $ts )->format( 'w' ) + 1
263  );
264  case 'localyear':
265  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'Y' ) );
266  case 'localtime':
267  return $pageLang->time(
268  self::makeTsLocal( $svcOptions, $ts )->format( 'YmdHis' ),
269  false,
270  false
271  );
272  case 'localhour':
273  return $pageLang->formatNumNoSeparators( self::makeTsLocal( $svcOptions, $ts )->format( 'H' ) );
274  case 'localweek':
275  // @bug T6594 PHP5 has it zero padded, PHP4 does not, cast to
276  // int to remove the padding
277  return $pageLang->formatNum( (int)self::makeTsLocal( $svcOptions, $ts )->format( 'W' ) );
278  case 'localdow':
279  return $pageLang->formatNum( self::makeTsLocal( $svcOptions, $ts )->format( 'w' ) );
280  case 'numberofarticles':
281  return $pageLang->formatNum( SiteStats::articles() );
282  case 'numberoffiles':
283  return $pageLang->formatNum( SiteStats::images() );
284  case 'numberofusers':
285  return $pageLang->formatNum( SiteStats::users() );
286  case 'numberofactiveusers':
287  return $pageLang->formatNum( SiteStats::activeUsers() );
288  case 'numberofpages':
289  return $pageLang->formatNum( SiteStats::pages() );
290  case 'numberofadmins':
291  return $pageLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
292  case 'numberofedits':
293  return $pageLang->formatNum( SiteStats::edits() );
294  case 'currenttimestamp':
295  return $ts->getTimestamp( TS_MW );
296  case 'localtimestamp':
297  return self::makeTsLocal( $svcOptions, $ts )->format( 'YmdHis' );
298  case 'currentversion':
300  case 'articlepath':
301  return (string)$svcOptions->get( MainConfigNames::ArticlePath );
302  case 'sitename':
303  return (string)$svcOptions->get( MainConfigNames::Sitename );
304  case 'server':
305  return (string)$svcOptions->get( MainConfigNames::Server );
306  case 'servername':
307  return (string)$svcOptions->get( MainConfigNames::ServerName );
308  case 'scriptpath':
309  return (string)$svcOptions->get( MainConfigNames::ScriptPath );
310  case 'stylepath':
311  return (string)$svcOptions->get( MainConfigNames::StylePath );
312  case 'directionmark':
313  return $pageLang->getDirMark();
314  case 'contentlanguage':
315  return $parser->getContentLanguage()->getCode();
316  case 'pagelanguage':
317  return $pageLang->getCode();
318  case 'cascadingsources':
319  return CoreParserFunctions::cascadingsources( $parser );
320  default:
321  // This is not one of the core magic variables
322  return null;
323  }
324  }
325 
333  private static function makeTsLocal( $svcOptions, $ts ) {
334  $localtimezone = $svcOptions->get( MainConfigNames::Localtimezone );
335  $ts->setTimezone( $localtimezone );
336  return $ts;
337  }
338 
348  private static function getRevisionTimestampSubstring(
349  Parser $parser,
350  LoggerInterface $logger,
351  int $start,
352  int $len,
353  int $mtts,
354  string $variable
355  ): string {
356  // Get the timezone-adjusted timestamp to be used for this revision
357  $resNow = substr( $parser->getRevisionTimestamp(), $start, $len );
358  // Possibly set vary-revision if there is not yet an associated revision
359  if ( !$parser->getRevisionRecordObject() ) {
360  // Get the timezone-adjusted timestamp $mtts seconds in the future.
361  // This future is relative to the current time and not that of the
362  // parser options. The rendered timestamp can be compared to that
363  // of the timestamp specified by the parser options.
364  $resThen = substr(
365  $parser->getContentLanguage()->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
366  $start,
367  $len
368  );
369 
370  if ( $resNow !== $resThen ) {
371  // Inform the edit saving system that getting the canonical output after
372  // revision insertion requires a parse that used an actual revision timestamp
373  self::setOutputFlag( $parser, $logger, ParserOutputFlags::VARY_REVISION_TIMESTAMP, "$variable used" );
374  }
375  }
376 
377  return $resNow;
378  }
379 
388  private static function setOutputFlag(
389  Parser $parser,
390  LoggerInterface $logger,
391  string $flag,
392  string $reason
393  ): void {
394  $parser->getOutput()->setOutputFlag( $flag );
395  $name = $parser->getTitle()->getPrefixedText();
396  // This code was moved from Parser::setOutputFlag and used __METHOD__
397  // originally; we've hard-coded that output here so that our refactor
398  // doesn't change the messages in the logs.
399  $logger->debug( "Parser::setOutputFlag: set $flag flag on '$name'; $reason" );
400  }
401 }
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Expansions of core magic variables, used by the parser.
static expand(Parser $parser, string $id, ConvertibleTimestamp $ts, NamespaceInfo $nsInfo, ServiceOptions $svcOptions, LoggerInterface $logger)
Expand the magic variable given by $index.
static cascadingsources( $parser, $title='')
Returns the sources of any cascading protection acting on a specified page.
A class for passing options to services.
A class containing constants representing the names of configuration variables.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:95
getOptions()
Definition: Parser.php:1124
getRevisionUser()
Get the name of the user that edited the last revision.
Definition: Parser.php:6101
getRevisionSize()
Get the size of the revision.
Definition: Parser.php:6125
getTitle()
Definition: Parser.php:1035
getContentLanguage()
Get the content language that this Parser is using.
Definition: Parser.php:1246
getOutput()
Definition: Parser.php:1116
getRevisionId()
Get the ID of the revision we are parsing.
Definition: Parser.php:6008
getRevisionRecordObject()
Get the revision record object for $this->mRevisionId.
Definition: Parser.php:6018
static articles()
Definition: SiteStats.php:104
static images()
Definition: SiteStats.php:140
static edits()
Definition: SiteStats.php:95
static users()
Definition: SiteStats.php:122
static pages()
Definition: SiteStats.php:113
static numberingroup( $group)
Find the number of users in a given user group.
Definition: SiteStats.php:151
static activeUsers()
Definition: SiteStats.php:131
static getVersion( $flags='', $lang=null)
Return a string of the MediaWiki version with Git revision if available.