MediaWiki  master
NamespaceInfo.php
Go to the documentation of this file.
1 <?php
28 
36 
43 
45  private $canonicalNamespaces = null;
46 
48  private $namespaceIndexes = false;
49 
51  private $validNamespaces = null;
52 
54  private $options;
55 
57  private $hookRunner;
58 
64  public const CANONICAL_NAMES = [
65  NS_MEDIA => 'Media',
66  NS_SPECIAL => 'Special',
67  NS_MAIN => '',
68  NS_TALK => 'Talk',
69  NS_USER => 'User',
70  NS_USER_TALK => 'User_talk',
71  NS_PROJECT => 'Project',
72  NS_PROJECT_TALK => 'Project_talk',
73  NS_FILE => 'File',
74  NS_FILE_TALK => 'File_talk',
75  NS_MEDIAWIKI => 'MediaWiki',
76  NS_MEDIAWIKI_TALK => 'MediaWiki_talk',
77  NS_TEMPLATE => 'Template',
78  NS_TEMPLATE_TALK => 'Template_talk',
79  NS_HELP => 'Help',
80  NS_HELP_TALK => 'Help_talk',
81  NS_CATEGORY => 'Category',
82  NS_CATEGORY_TALK => 'Category_talk',
83  ];
84 
89  public const CONSTRUCTOR_OPTIONS = [
90  'AllowImageMoving',
91  'CanonicalNamespaceNames',
92  'CapitalLinkOverrides',
93  'CapitalLinks',
94  'ContentNamespaces',
95  'ExtraNamespaces',
96  'ExtraSignatureNamespaces',
97  'NamespaceContentModels',
98  'NamespacesWithSubpages',
99  'NonincludableNamespaces',
100  ];
101 
106  public function __construct( ServiceOptions $options, HookContainer $hookContainer ) {
107  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
108  $this->options = $options;
109  $this->hookRunner = new HookRunner( $hookContainer );
110  }
111 
124  private function isMethodValidFor( $index, $method ) {
125  if ( $index < NS_MAIN ) {
126  throw new MWException( "$method does not make any sense for given namespace $index" );
127  }
128  return true;
129  }
130 
140  private function makeValidNamespace( $index, $method ) {
141  if ( !(
142  is_int( $index )
143  // Namespace index numbers as strings
144  || ctype_digit( $index )
145  // Negative numbers as strings
146  || ( $index[0] === '-' && ctype_digit( substr( $index, 1 ) ) )
147  ) ) {
148  throw new InvalidArgumentException(
149  "$method called with non-integer (" . gettype( $index ) . ") namespace '$index'"
150  );
151  }
152 
153  return intval( $index );
154  }
155 
162  public function isMovable( $index ) {
163  if ( !$this->options->get( 'AllowImageMoving' ) ) {
164  wfDeprecatedMsg( 'Setting $wgAllowImageMoving to false was deprecated in MediaWiki 1.35',
165  '1.35', false, false );
166  }
167 
168  $result = $index >= NS_MAIN &&
169  ( $index != NS_FILE || $this->options->get( 'AllowImageMoving' ) );
170 
174  $this->hookRunner->onNamespaceIsMovable( $index, $result );
175 
176  return $result;
177  }
178 
185  public function isSubject( $index ) {
186  return !$this->isTalk( $index );
187  }
188 
195  public function isTalk( $index ) {
196  $index = $this->makeValidNamespace( $index, __METHOD__ );
197 
198  return $index > NS_MAIN
199  && $index % 2;
200  }
201 
210  public function getTalk( $index ) {
211  $index = $this->makeValidNamespace( $index, __METHOD__ );
212 
213  $this->isMethodValidFor( $index, __METHOD__ );
214  return $this->isTalk( $index )
215  ? $index
216  : $index + 1;
217  }
218 
228  public function getTalkPage( LinkTarget $target ) : LinkTarget {
229  if ( $target->getText() === '' ) {
230  throw new MWException( 'Can\'t determine talk page associated with relative section link' );
231  }
232 
233  if ( $target->getInterwiki() !== '' ) {
234  throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
235  }
236 
237  if ( $this->isTalk( $target->getNamespace() ) ) {
238  return $target;
239  }
240 
241  // NOTE: getTalk throws on bad namespaces!
242  return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
243  }
244 
256  public function canHaveTalkPage( LinkTarget $target ) {
257  if ( $target->getText() === '' || $target->getInterwiki() !== '' ) {
258  return false;
259  }
260 
261  if ( $target->getNamespace() < NS_MAIN ) {
262  return false;
263  }
264 
265  return true;
266  }
267 
275  public function getSubject( $index ) {
276  $index = $this->makeValidNamespace( $index, __METHOD__ );
277 
278  # Handle special namespaces
279  if ( $index < NS_MAIN ) {
280  return $index;
281  }
282 
283  return $this->isTalk( $index )
284  ? $index - 1
285  : $index;
286  }
287 
292  public function getSubjectPage( LinkTarget $target ) : LinkTarget {
293  if ( $this->isSubject( $target->getNamespace() ) ) {
294  return $target;
295  }
296  return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
297  }
298 
308  public function getAssociated( $index ) {
309  $this->isMethodValidFor( $index, __METHOD__ );
310 
311  if ( $this->isSubject( $index ) ) {
312  return $this->getTalk( $index );
313  }
314  return $this->getSubject( $index );
315  }
316 
323  public function getAssociatedPage( LinkTarget $target ) : LinkTarget {
324  if ( $target->getText() === '' ) {
325  throw new MWException( 'Can\'t determine talk page associated with relative section link' );
326  }
327 
328  if ( $target->getInterwiki() !== '' ) {
329  throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
330  }
331 
332  return new TitleValue(
333  $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
334  }
335 
343  public function exists( $index ) {
344  $nslist = $this->getCanonicalNamespaces();
345  return isset( $nslist[$index] );
346  }
347 
361  public function equals( $ns1, $ns2 ) {
362  return $ns1 == $ns2;
363  }
364 
375  public function subjectEquals( $ns1, $ns2 ) {
376  return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
377  }
378 
385  public function getCanonicalNamespaces() {
386  if ( $this->canonicalNamespaces === null ) {
387  $this->canonicalNamespaces =
388  [ NS_MAIN => '' ] + $this->options->get( 'CanonicalNamespaceNames' );
389  $this->canonicalNamespaces +=
390  ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
391  if ( is_array( $this->options->get( 'ExtraNamespaces' ) ) ) {
392  $this->canonicalNamespaces += $this->options->get( 'ExtraNamespaces' );
393  }
394  $this->hookRunner->onCanonicalNamespaces( $this->canonicalNamespaces );
395  }
397  }
398 
405  public function getCanonicalName( $index ) {
406  $nslist = $this->getCanonicalNamespaces();
407  return $nslist[$index] ?? false;
408  }
409 
417  public function getCanonicalIndex( $name ) {
418  if ( $this->namespaceIndexes === false ) {
419  $this->namespaceIndexes = [];
420  foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
421  $this->namespaceIndexes[strtolower( $text )] = $i;
422  }
423  }
424  if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
425  return $this->namespaceIndexes[$name];
426  } else {
427  return null;
428  }
429  }
430 
436  public function getValidNamespaces() {
437  if ( $this->validNamespaces === null ) {
438  $this->validNamespaces = [];
439  foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
440  if ( $ns >= 0 ) {
441  $this->validNamespaces[] = $ns;
442  }
443  }
444  // T109137: sort numerically
445  sort( $this->validNamespaces, SORT_NUMERIC );
446  }
447 
448  return $this->validNamespaces;
449  }
450 
457  public function hasTalkNamespace( $index ) {
458  return $index >= NS_MAIN;
459  }
460 
468  public function isContent( $index ) {
469  return $index == NS_MAIN || in_array( $index, $this->options->get( 'ContentNamespaces' ) );
470  }
471 
479  public function wantSignatures( $index ) {
480  return $this->isTalk( $index ) ||
481  in_array( $index, $this->options->get( 'ExtraSignatureNamespaces' ) );
482  }
483 
490  public function isWatchable( $index ) {
491  return $index >= NS_MAIN;
492  }
493 
500  public function hasSubpages( $index ) {
501  return !empty( $this->options->get( 'NamespacesWithSubpages' )[$index] );
502  }
503 
508  public function getContentNamespaces() {
509  $contentNamespaces = $this->options->get( 'ContentNamespaces' );
510  if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
511  return [ NS_MAIN ];
512  } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
513  // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
514  return array_merge( [ NS_MAIN ], $contentNamespaces );
515  } else {
516  return $contentNamespaces;
517  }
518  }
519 
526  public function getSubjectNamespaces() {
527  return array_filter(
528  $this->getValidNamespaces(),
529  [ $this, 'isSubject' ]
530  );
531  }
532 
539  public function getTalkNamespaces() {
540  return array_filter(
541  $this->getValidNamespaces(),
542  [ $this, 'isTalk' ]
543  );
544  }
545 
552  public function isCapitalized( $index ) {
553  // Turn NS_MEDIA into NS_FILE
554  $index = $index === NS_MEDIA ? NS_FILE : $index;
555 
556  // Make sure to get the subject of our namespace
557  $index = $this->getSubject( $index );
558 
559  // Some namespaces are special and should always be upper case
560  if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
561  return true;
562  }
563  $overrides = $this->options->get( 'CapitalLinkOverrides' );
564  if ( isset( $overrides[$index] ) ) {
565  // CapitalLinkOverrides is explicitly set
566  return $overrides[$index];
567  }
568  // Default to the global setting
569  return $this->options->get( 'CapitalLinks' );
570  }
571 
579  public function hasGenderDistinction( $index ) {
580  return $index == NS_USER || $index == NS_USER_TALK;
581  }
582 
589  public function isNonincludable( $index ) {
590  $namespaces = $this->options->get( 'NonincludableNamespaces' );
591  return $namespaces && in_array( $index, $namespaces );
592  }
593 
604  public function getNamespaceContentModel( $index ) {
605  return $this->options->get( 'NamespaceContentModels' )[$index] ?? null;
606  }
607 
617  public function getRestrictionLevels( $index, User $user = null ) {
618  // PermissionManager is not injected because adding an explicit dependency
619  // breaks MW installer by adding a dependency chain on the database before
620  // it was set up. Also, the method is deprecated and will be soon removed.
621  return MediaWikiServices::getInstance()
622  ->getPermissionManager()
623  ->getNamespaceRestrictionLevels( $index, $user );
624  }
625 
635  public function getCategoryLinkType( $index ) {
636  $this->isMethodValidFor( $index, __METHOD__ );
637 
638  if ( $index == NS_CATEGORY ) {
639  return 'subcat';
640  } elseif ( $index == NS_FILE ) {
641  return 'file';
642  } else {
643  return 'page';
644  }
645  }
646 
654  public static function getCommonNamespaces() {
655  return array_keys( self::CANONICAL_NAMES );
656  }
657 }
MediaWiki\Linker\LinkTarget\getInterwiki
getInterwiki()
The interwiki component of this LinkTarget.
MediaWiki\Linker\LinkTarget\getText
getText()
Returns the link in text form, without namespace prefix or fragment.
NamespaceInfo\isMovable
isMovable( $index)
Can pages in the given namespace be moved?
Definition: NamespaceInfo.php:162
NS_HELP
const NS_HELP
Definition: Defines.php:81
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
NS_TEMPLATE_TALK
const NS_TEMPLATE_TALK
Definition: Defines.php:80
NamespaceInfo\isWatchable
isWatchable( $index)
Can pages in a namespace be watched?
Definition: NamespaceInfo.php:490
NamespaceInfo\makeValidNamespace
makeValidNamespace( $index, $method)
Throw if given index isn't an integer or integer-like string and so can't be a valid namespace.
Definition: NamespaceInfo.php:140
NamespaceInfo\getAssociatedPage
getAssociatedPage(LinkTarget $target)
Definition: NamespaceInfo.php:323
NamespaceInfo\hasTalkNamespace
hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
Definition: NamespaceInfo.php:457
NamespaceInfo\getValidNamespaces
getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: NamespaceInfo.php:436
NamespaceInfo\CANONICAL_NAMES
const CANONICAL_NAMES
Definitions of the NS_ constants are in Defines.php.
Definition: NamespaceInfo.php:64
NS_FILE
const NS_FILE
Definition: Defines.php:75
NamespaceInfo\getCanonicalNamespaces
getCanonicalNamespaces()
Returns array of all defined namespaces with their canonical (English) names.
Definition: NamespaceInfo.php:385
NS_TEMPLATE
const NS_TEMPLATE
Definition: Defines.php:79
NamespaceInfo\getCategoryLinkType
getCategoryLinkType( $index)
Returns the link type to be used for categories.
Definition: NamespaceInfo.php:635
NamespaceInfo\wantSignatures
wantSignatures( $index)
Might pages in this namespace require the use of the Signature button on the edit toolbar?
Definition: NamespaceInfo.php:479
NamespaceInfo\$options
ServiceOptions $options
Definition: NamespaceInfo.php:54
NamespaceInfo\subjectEquals
subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
Definition: NamespaceInfo.php:375
NamespaceInfo\isSubject
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?
Definition: NamespaceInfo.php:185
NamespaceInfo\$canonicalNamespaces
string[] null $canonicalNamespaces
Canonical namespaces cache.
Definition: NamespaceInfo.php:45
NamespaceInfo\$namespaceIndexes
array false $namespaceIndexes
Canonical namespaces index cache.
Definition: NamespaceInfo.php:48
NamespaceInfo\hasSubpages
hasSubpages( $index)
Does the namespace allow subpages?
Definition: NamespaceInfo.php:500
NS_MAIN
const NS_MAIN
Definition: Defines.php:69
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:136
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:58
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
NamespaceInfo\$hookRunner
HookRunner $hookRunner
Definition: NamespaceInfo.php:57
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1058
MWException
MediaWiki exception.
Definition: MWException.php:29
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:73
NamespaceInfo\getSubjectNamespaces
getSubjectNamespaces()
List all namespace indices which are considered subject, aka not a talk or special namespace.
Definition: NamespaceInfo.php:526
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:25
NamespaceInfo\getSubject
getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: NamespaceInfo.php:275
NamespaceInfo\getSubjectPage
getSubjectPage(LinkTarget $target)
Definition: NamespaceInfo.php:292
NamespaceInfo\getContentNamespaces
getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Definition: NamespaceInfo.php:508
NamespaceInfo\canHaveTalkPage
canHaveTalkPage(LinkTarget $target)
Can the title have a corresponding talk page?
Definition: NamespaceInfo.php:256
NS_MEDIAWIKI_TALK
const NS_MEDIAWIKI_TALK
Definition: Defines.php:78
NamespaceInfo\getTalkNamespaces
getTalkNamespaces()
List all namespace indices which are considered talks, aka not a subject or special namespace.
Definition: NamespaceInfo.php:539
NamespaceInfo\isContent
isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
Definition: NamespaceInfo.php:468
NamespaceInfo\getTalk
getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: NamespaceInfo.php:210
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:83
NamespaceInfo\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: NamespaceInfo.php:89
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:72
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:57
NamespaceInfo\exists
exists( $index)
Returns whether the specified namespace exists.
Definition: NamespaceInfo.php:343
NamespaceInfo\$alwaysCapitalizedNamespaces
$alwaysCapitalizedNamespaces
These namespaces should always be first-letter capitalized, now and forevermore.
Definition: NamespaceInfo.php:42
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
NS_PROJECT_TALK
const NS_PROJECT_TALK
Definition: Defines.php:74
NamespaceInfo\getCanonicalName
getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: NamespaceInfo.php:405
NamespaceInfo\isTalk
isTalk( $index)
Is the given namespace a talk namespace?
Definition: NamespaceInfo.php:195
NamespaceInfo\__construct
__construct(ServiceOptions $options, HookContainer $hookContainer)
Definition: NamespaceInfo.php:106
NamespaceInfo\getNamespaceContentModel
getNamespaceContentModel( $index)
Get the default content model for a namespace This does not mean that all pages in that namespace hav...
Definition: NamespaceInfo.php:604
NamespaceInfo\hasGenderDistinction
hasGenderDistinction( $index)
Does the namespace (potentially) have different aliases for different genders.
Definition: NamespaceInfo.php:579
NamespaceInfo\equals
equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
Definition: NamespaceInfo.php:361
NamespaceInfo\isNonincludable
isNonincludable( $index)
It is not possible to use pages from this namespace as template?
Definition: NamespaceInfo.php:589
NamespaceInfo\getCanonicalIndex
getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
Definition: NamespaceInfo.php:417
NS_HELP_TALK
const NS_HELP_TALK
Definition: Defines.php:82
NamespaceInfo\isCapitalized
isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: NamespaceInfo.php:552
NS_USER
const NS_USER
Definition: Defines.php:71
NS_TALK
const NS_TALK
Definition: Defines.php:70
NamespaceInfo\getTalkPage
getTalkPage(LinkTarget $target)
Get a LinkTarget referring to the talk page of $target.
Definition: NamespaceInfo.php:228
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:44
NamespaceInfo
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Definition: NamespaceInfo.php:35
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:77
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:570
NamespaceInfo\$validNamespaces
int[] null $validNamespaces
Valid namespaces cache.
Definition: NamespaceInfo.php:51
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
NS_FILE_TALK
const NS_FILE_TALK
Definition: Defines.php:76
NS_CATEGORY_TALK
const NS_CATEGORY_TALK
Definition: Defines.php:84
NamespaceInfo\getCommonNamespaces
static getCommonNamespaces()
Retrieve the indexes for the namespaces defined by core.
Definition: NamespaceInfo.php:654
NamespaceInfo\isMethodValidFor
isMethodValidFor( $index, $method)
Throw an exception when trying to get the subject or talk page for a given namespace where it does no...
Definition: NamespaceInfo.php:124
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
NamespaceInfo\getAssociated
getAssociated( $index)
Get the associated namespace.
Definition: NamespaceInfo.php:308
NamespaceInfo\getRestrictionLevels
getRestrictionLevels( $index, User $user=null)
Determine which restriction levels it makes sense to use in a namespace, optionally filtered by a use...
Definition: NamespaceInfo.php:617
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:39