MediaWiki REL1_35
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 $extensionRegistry = ExtensionRegistry::getInstance();
169 $extNamespaces = $extensionRegistry->getAttribute( 'ImmovableNamespaces' );
170
171 $result = $index >= NS_MAIN &&
172 ( $index != NS_FILE || $this->options->get( 'AllowImageMoving' ) ) &&
173 !in_array( $index, $extNamespaces );
174
178 $this->hookRunner->onNamespaceIsMovable( $index, $result );
179
180 return $result;
181 }
182
189 public function isSubject( $index ) {
190 return !$this->isTalk( $index );
191 }
192
199 public function isTalk( $index ) {
200 $index = $this->makeValidNamespace( $index, __METHOD__ );
201
202 return $index > NS_MAIN
203 && $index % 2;
204 }
205
214 public function getTalk( $index ) {
215 $index = $this->makeValidNamespace( $index, __METHOD__ );
216
217 $this->isMethodValidFor( $index, __METHOD__ );
218 return $this->isTalk( $index )
219 ? $index
220 : $index + 1;
221 }
222
232 public function getTalkPage( LinkTarget $target ) : LinkTarget {
233 if ( $target->getText() === '' ) {
234 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
235 }
236
237 if ( $target->getInterwiki() !== '' ) {
238 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
239 }
240
241 if ( $this->isTalk( $target->getNamespace() ) ) {
242 return $target;
243 }
244
245 // NOTE: getTalk throws on bad namespaces!
246 return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
247 }
248
260 public function canHaveTalkPage( LinkTarget $target ) {
261 if ( $target->getText() === '' || $target->getInterwiki() !== '' ) {
262 return false;
263 }
264
265 if ( $target->getNamespace() < NS_MAIN ) {
266 return false;
267 }
268
269 return true;
270 }
271
279 public function getSubject( $index ) {
280 $index = $this->makeValidNamespace( $index, __METHOD__ );
281
282 # Handle special namespaces
283 if ( $index < NS_MAIN ) {
284 return $index;
285 }
286
287 return $this->isTalk( $index )
288 ? $index - 1
289 : $index;
290 }
291
296 public function getSubjectPage( LinkTarget $target ) : LinkTarget {
297 if ( $this->isSubject( $target->getNamespace() ) ) {
298 return $target;
299 }
300 return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
301 }
302
312 public function getAssociated( $index ) {
313 $this->isMethodValidFor( $index, __METHOD__ );
314
315 if ( $this->isSubject( $index ) ) {
316 return $this->getTalk( $index );
317 }
318 return $this->getSubject( $index );
319 }
320
327 public function getAssociatedPage( LinkTarget $target ) : LinkTarget {
328 if ( $target->getText() === '' ) {
329 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
330 }
331
332 if ( $target->getInterwiki() !== '' ) {
333 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
334 }
335
336 return new TitleValue(
337 $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
338 }
339
347 public function exists( $index ) {
348 $nslist = $this->getCanonicalNamespaces();
349 return isset( $nslist[$index] );
350 }
351
365 public function equals( $ns1, $ns2 ) {
366 return $ns1 == $ns2;
367 }
368
379 public function subjectEquals( $ns1, $ns2 ) {
380 return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
381 }
382
389 public function getCanonicalNamespaces() {
390 if ( $this->canonicalNamespaces === null ) {
391 $this->canonicalNamespaces =
392 [ NS_MAIN => '' ] + $this->options->get( 'CanonicalNamespaceNames' );
393 $this->canonicalNamespaces +=
394 ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
395 if ( is_array( $this->options->get( 'ExtraNamespaces' ) ) ) {
396 $this->canonicalNamespaces += $this->options->get( 'ExtraNamespaces' );
397 }
398 $this->hookRunner->onCanonicalNamespaces( $this->canonicalNamespaces );
399 }
400 return $this->canonicalNamespaces;
401 }
402
409 public function getCanonicalName( $index ) {
410 $nslist = $this->getCanonicalNamespaces();
411 return $nslist[$index] ?? false;
412 }
413
421 public function getCanonicalIndex( $name ) {
422 if ( $this->namespaceIndexes === false ) {
423 $this->namespaceIndexes = [];
424 foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
425 $this->namespaceIndexes[strtolower( $text )] = $i;
426 }
427 }
428 if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
429 return $this->namespaceIndexes[$name];
430 } else {
431 return null;
432 }
433 }
434
440 public function getValidNamespaces() {
441 if ( $this->validNamespaces === null ) {
442 $this->validNamespaces = [];
443 foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
444 if ( $ns >= 0 ) {
445 $this->validNamespaces[] = $ns;
446 }
447 }
448 // T109137: sort numerically
449 sort( $this->validNamespaces, SORT_NUMERIC );
450 }
451
452 return $this->validNamespaces;
453 }
454
461 public function hasTalkNamespace( $index ) {
462 return $index >= NS_MAIN;
463 }
464
472 public function isContent( $index ) {
473 return $index == NS_MAIN || in_array( $index, $this->options->get( 'ContentNamespaces' ) );
474 }
475
483 public function wantSignatures( $index ) {
484 return $this->isTalk( $index ) ||
485 in_array( $index, $this->options->get( 'ExtraSignatureNamespaces' ) );
486 }
487
494 public function isWatchable( $index ) {
495 return $index >= NS_MAIN;
496 }
497
504 public function hasSubpages( $index ) {
505 return !empty( $this->options->get( 'NamespacesWithSubpages' )[$index] );
506 }
507
512 public function getContentNamespaces() {
513 $contentNamespaces = $this->options->get( 'ContentNamespaces' );
514 if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
515 return [ NS_MAIN ];
516 } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
517 // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
518 return array_merge( [ NS_MAIN ], $contentNamespaces );
519 } else {
520 return $contentNamespaces;
521 }
522 }
523
530 public function getSubjectNamespaces() {
531 return array_filter(
532 $this->getValidNamespaces(),
533 [ $this, 'isSubject' ]
534 );
535 }
536
543 public function getTalkNamespaces() {
544 return array_filter(
545 $this->getValidNamespaces(),
546 [ $this, 'isTalk' ]
547 );
548 }
549
556 public function isCapitalized( $index ) {
557 // Turn NS_MEDIA into NS_FILE
558 $index = $index === NS_MEDIA ? NS_FILE : $index;
559
560 // Make sure to get the subject of our namespace
561 $index = $this->getSubject( $index );
562
563 // Some namespaces are special and should always be upper case
564 if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
565 return true;
566 }
567 $overrides = $this->options->get( 'CapitalLinkOverrides' );
568 if ( isset( $overrides[$index] ) ) {
569 // CapitalLinkOverrides is explicitly set
570 return $overrides[$index];
571 }
572 // Default to the global setting
573 return $this->options->get( 'CapitalLinks' );
574 }
575
583 public function hasGenderDistinction( $index ) {
584 return $index == NS_USER || $index == NS_USER_TALK;
585 }
586
593 public function isNonincludable( $index ) {
594 $namespaces = $this->options->get( 'NonincludableNamespaces' );
595 return $namespaces && in_array( $index, $namespaces );
596 }
597
608 public function getNamespaceContentModel( $index ) {
609 return $this->options->get( 'NamespaceContentModels' )[$index] ?? null;
610 }
611
621 public function getRestrictionLevels( $index, User $user = null ) {
622 // PermissionManager is not injected because adding an explicit dependency
623 // breaks MW installer by adding a dependency chain on the database before
624 // it was set up. Also, the method is deprecated and will be soon removed.
625 return MediaWikiServices::getInstance()
626 ->getPermissionManager()
627 ->getNamespaceRestrictionLevels( $index, $user );
628 }
629
639 public function getCategoryLinkType( $index ) {
640 $this->isMethodValidFor( $index, __METHOD__ );
641
642 if ( $index == NS_CATEGORY ) {
643 return 'subcat';
644 } elseif ( $index == NS_FILE ) {
645 return 'file';
646 } else {
647 return 'page';
648 }
649 }
650
658 public static function getCommonNamespaces() {
659 return array_keys( self::CANONICAL_NAMES );
660 }
661}
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition Setup.php:85
MediaWiki exception.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
MediaWikiServices is the service locator for the application scope of MediaWiki.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
getAssociatedPage(LinkTarget $target)
ServiceOptions $options
hasGenderDistinction( $index)
Does the namespace (potentially) have different aliases for different genders.
__construct(ServiceOptions $options, HookContainer $hookContainer)
equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
wantSignatures( $index)
Might pages in this namespace require the use of the Signature button on the edit toolbar?
getNamespaceContentModel( $index)
Get the default content model for a namespace This does not mean that all pages in that namespace hav...
subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
isCapitalized( $index)
Is the namespace first-letter capitalized?
getCanonicalName( $index)
Returns the canonical (English) name for a given index.
getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
string[] null $canonicalNamespaces
Canonical namespaces cache.
isNonincludable( $index)
It is not possible to use pages from this namespace as template?
getTalkNamespaces()
List all namespace indices which are considered talks, aka not a subject or special namespace.
getRestrictionLevels( $index, User $user=null)
Determine which restriction levels it makes sense to use in a namespace, optionally filtered by a use...
makeValidNamespace( $index, $method)
Throw if given index isn't an integer or integer-like string and so can't be a valid namespace.
hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?
HookRunner $hookRunner
$alwaysCapitalizedNamespaces
These namespaces should always be first-letter capitalized, now and forevermore.
getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
static getCommonNamespaces()
Retrieve the indexes for the namespaces defined by core.
exists( $index)
Returns whether the specified namespace exists.
isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
getSubjectPage(LinkTarget $target)
isMethodValidFor( $index, $method)
Throw an exception when trying to get the subject or talk page for a given namespace where it does no...
int[] null $validNamespaces
Valid namespaces cache.
getCanonicalNamespaces()
Returns array of all defined namespaces with their canonical (English) names.
const CANONICAL_NAMES
Definitions of the NS_ constants are in Defines.php.
getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
hasSubpages( $index)
Does the namespace allow subpages?
array false $namespaceIndexes
Canonical namespaces index cache.
canHaveTalkPage(LinkTarget $target)
Can the title have a corresponding talk page?
isWatchable( $index)
Can pages in a namespace be watched?
getTalk( $index)
Get the talk namespace index for a given namespace.
getSubjectNamespaces()
List all namespace indices which are considered subject, aka not a talk or special namespace.
isMovable( $index)
Can pages in the given namespace be moved?
getCategoryLinkType( $index)
Returns the link type to be used for categories.
getAssociated( $index)
Get the associated namespace.
getTalkPage(LinkTarget $target)
Get a LinkTarget referring to the talk page of $target.
const CONSTRUCTOR_OPTIONS
isTalk( $index)
Is the given namespace a talk namespace?
getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
Represents a page (or page fragment) title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
const NS_HELP
Definition Defines.php:82
const NS_USER
Definition Defines.php:72
const NS_FILE
Definition Defines.php:76
const NS_MEDIAWIKI_TALK
Definition Defines.php:79
const NS_MAIN
Definition Defines.php:70
const NS_PROJECT_TALK
Definition Defines.php:75
const NS_MEDIAWIKI
Definition Defines.php:78
const NS_TEMPLATE
Definition Defines.php:80
const NS_SPECIAL
Definition Defines.php:59
const NS_FILE_TALK
Definition Defines.php:77
const NS_HELP_TALK
Definition Defines.php:83
const NS_CATEGORY_TALK
Definition Defines.php:85
const NS_MEDIA
Definition Defines.php:58
const NS_TALK
Definition Defines.php:71
const NS_USER_TALK
Definition Defines.php:73
const NS_PROJECT
Definition Defines.php:74
const NS_CATEGORY
Definition Defines.php:84
const NS_TEMPLATE_TALK
Definition Defines.php:81
getInterwiki()
The interwiki component of this LinkTarget.
getNamespace()
Get the namespace index.
getText()
Returns the link in text form, without namespace prefix or fragment.