MediaWiki REL1_37
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
88 public const CONSTRUCTOR_OPTIONS = [
89 'CanonicalNamespaceNames',
90 'CapitalLinkOverrides',
91 'CapitalLinks',
92 'ContentNamespaces',
93 'ExtraNamespaces',
94 'ExtraSignatureNamespaces',
95 'NamespaceContentModels',
96 'NamespacesWithSubpages',
97 'NonincludableNamespaces',
98 ];
99
104 public function __construct( ServiceOptions $options, HookContainer $hookContainer ) {
105 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
106 $this->options = $options;
107 $this->hookRunner = new HookRunner( $hookContainer );
108 }
109
122 private function isMethodValidFor( $index, $method ) {
123 if ( $index < NS_MAIN ) {
124 throw new MWException( "$method does not make any sense for given namespace $index" );
125 }
126 return true;
127 }
128
138 private function makeValidNamespace( $index, $method ) {
139 if ( !(
140 is_int( $index )
141 // Namespace index numbers as strings
142 || ctype_digit( $index )
143 // Negative numbers as strings
144 || ( $index[0] === '-' && ctype_digit( substr( $index, 1 ) ) )
145 ) ) {
146 throw new InvalidArgumentException(
147 "$method called with non-integer (" . gettype( $index ) . ") namespace '$index'"
148 );
149 }
150
151 return intval( $index );
152 }
153
160 public function isMovable( $index ) {
161 $extensionRegistry = ExtensionRegistry::getInstance();
162 $extNamespaces = $extensionRegistry->getAttribute( 'ImmovableNamespaces' );
163
164 $result = $index >= NS_MAIN && !in_array( $index, $extNamespaces );
165
169 $this->hookRunner->onNamespaceIsMovable( $index, $result );
170
171 return $result;
172 }
173
180 public function isSubject( $index ) {
181 return !$this->isTalk( $index );
182 }
183
190 public function isTalk( $index ) {
191 $index = $this->makeValidNamespace( $index, __METHOD__ );
192
193 return $index > NS_MAIN
194 && $index % 2;
195 }
196
205 public function getTalk( $index ) {
206 $index = $this->makeValidNamespace( $index, __METHOD__ );
207
208 $this->isMethodValidFor( $index, __METHOD__ );
209 return $this->isTalk( $index )
210 ? $index
211 : $index + 1;
212 }
213
223 public function getTalkPage( LinkTarget $target ): LinkTarget {
224 if ( $target->getText() === '' ) {
225 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
226 }
227
228 if ( $target->getInterwiki() !== '' ) {
229 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
230 }
231
232 if ( $this->isTalk( $target->getNamespace() ) ) {
233 return $target;
234 }
235
236 // NOTE: getTalk throws on bad namespaces!
237 return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
238 }
239
251 public function canHaveTalkPage( LinkTarget $target ) {
252 if ( $target->getText() === '' || $target->getInterwiki() !== '' ) {
253 return false;
254 }
255
256 if ( $target->getNamespace() < NS_MAIN ) {
257 return false;
258 }
259
260 return true;
261 }
262
270 public function getSubject( $index ) {
271 $index = $this->makeValidNamespace( $index, __METHOD__ );
272
273 # Handle special namespaces
274 if ( $index < NS_MAIN ) {
275 return $index;
276 }
277
278 return $this->isTalk( $index )
279 ? $index - 1
280 : $index;
281 }
282
287 public function getSubjectPage( LinkTarget $target ): LinkTarget {
288 if ( $this->isSubject( $target->getNamespace() ) ) {
289 return $target;
290 }
291 return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
292 }
293
303 public function getAssociated( $index ) {
304 $this->isMethodValidFor( $index, __METHOD__ );
305
306 if ( $this->isSubject( $index ) ) {
307 return $this->getTalk( $index );
308 }
309 return $this->getSubject( $index );
310 }
311
318 public function getAssociatedPage( LinkTarget $target ): LinkTarget {
319 if ( $target->getText() === '' ) {
320 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
321 }
322
323 if ( $target->getInterwiki() !== '' ) {
324 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
325 }
326
327 return new TitleValue(
328 $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
329 }
330
338 public function exists( $index ) {
339 $nslist = $this->getCanonicalNamespaces();
340 return isset( $nslist[$index] );
341 }
342
356 public function equals( $ns1, $ns2 ) {
357 return $ns1 == $ns2;
358 }
359
370 public function subjectEquals( $ns1, $ns2 ) {
371 return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
372 }
373
380 public function getCanonicalNamespaces() {
381 if ( $this->canonicalNamespaces === null ) {
382 $this->canonicalNamespaces =
383 [ NS_MAIN => '' ] + $this->options->get( 'CanonicalNamespaceNames' );
384 $this->canonicalNamespaces +=
385 ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
386 if ( is_array( $this->options->get( 'ExtraNamespaces' ) ) ) {
387 $this->canonicalNamespaces += $this->options->get( 'ExtraNamespaces' );
388 }
389 $this->hookRunner->onCanonicalNamespaces( $this->canonicalNamespaces );
390 }
391 return $this->canonicalNamespaces;
392 }
393
400 public function getCanonicalName( $index ) {
401 $nslist = $this->getCanonicalNamespaces();
402 return $nslist[$index] ?? false;
403 }
404
412 public function getCanonicalIndex( $name ) {
413 if ( $this->namespaceIndexes === false ) {
414 $this->namespaceIndexes = [];
415 foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
416 $this->namespaceIndexes[strtolower( $text )] = $i;
417 }
418 }
419 if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
420 return $this->namespaceIndexes[$name];
421 } else {
422 return null;
423 }
424 }
425
431 public function getValidNamespaces() {
432 if ( $this->validNamespaces === null ) {
433 $this->validNamespaces = [];
434 foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
435 if ( $ns >= 0 ) {
436 $this->validNamespaces[] = $ns;
437 }
438 }
439 // T109137: sort numerically
440 sort( $this->validNamespaces, SORT_NUMERIC );
441 }
442
443 return $this->validNamespaces;
444 }
445
452 public function hasTalkNamespace( $index ) {
453 return $index >= NS_MAIN;
454 }
455
463 public function isContent( $index ) {
464 return $index == NS_MAIN || in_array( $index, $this->options->get( 'ContentNamespaces' ) );
465 }
466
474 public function wantSignatures( $index ) {
475 return $this->isTalk( $index ) ||
476 in_array( $index, $this->options->get( 'ExtraSignatureNamespaces' ) );
477 }
478
485 public function isWatchable( $index ) {
486 return $index >= NS_MAIN;
487 }
488
496 public function hasSubpages( $index ) {
497 return !empty( $this->options->get( 'NamespacesWithSubpages' )[$index] );
498 }
499
504 public function getContentNamespaces() {
505 $contentNamespaces = $this->options->get( 'ContentNamespaces' );
506 if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
507 return [ NS_MAIN ];
508 } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
509 // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
510 return array_merge( [ NS_MAIN ], $contentNamespaces );
511 } else {
512 return $contentNamespaces;
513 }
514 }
515
522 public function getSubjectNamespaces() {
523 return array_filter(
524 $this->getValidNamespaces(),
525 [ $this, 'isSubject' ]
526 );
527 }
528
535 public function getTalkNamespaces() {
536 return array_filter(
537 $this->getValidNamespaces(),
538 [ $this, 'isTalk' ]
539 );
540 }
541
548 public function isCapitalized( $index ) {
549 // Turn NS_MEDIA into NS_FILE
550 $index = $index === NS_MEDIA ? NS_FILE : $index;
551
552 // Make sure to get the subject of our namespace
553 $index = $this->getSubject( $index );
554
555 // Some namespaces are special and should always be upper case
556 if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
557 return true;
558 }
559 $overrides = $this->options->get( 'CapitalLinkOverrides' );
560 if ( isset( $overrides[$index] ) ) {
561 // CapitalLinkOverrides is explicitly set
562 return $overrides[$index];
563 }
564 // Default to the global setting
565 return $this->options->get( 'CapitalLinks' );
566 }
567
575 public function hasGenderDistinction( $index ) {
576 return $index == NS_USER || $index == NS_USER_TALK;
577 }
578
585 public function isNonincludable( $index ) {
586 $namespaces = $this->options->get( 'NonincludableNamespaces' );
587 return $namespaces && in_array( $index, $namespaces );
588 }
589
600 public function getNamespaceContentModel( $index ) {
601 return $this->options->get( 'NamespaceContentModels' )[$index] ?? null;
602 }
603
613 public function getRestrictionLevels( $index, User $user = null ) {
614 // PermissionManager is not injected because adding an explicit dependency
615 // breaks MW installer by adding a dependency chain on the database before
616 // it was set up. Also, the method is deprecated and will be soon removed.
617 wfDeprecated( __METHOD__, '1.34' );
618 return MediaWikiServices::getInstance()
619 ->getPermissionManager()
620 ->getNamespaceRestrictionLevels( $index, $user );
621 }
622
632 public function getCategoryLinkType( $index ) {
633 $this->isMethodValidFor( $index, __METHOD__ );
634
635 if ( $index == NS_CATEGORY ) {
636 return 'subcat';
637 } elseif ( $index == NS_FILE ) {
638 return 'file';
639 } else {
640 return 'page';
641 }
642 }
643
651 public static function getCommonNamespaces() {
652 return array_keys( self::CANONICAL_NAMES );
653 }
654}
const NS_HELP
Definition Defines.php:76
const NS_USER
Definition Defines.php:66
const NS_FILE
Definition Defines.php:70
const NS_MEDIAWIKI_TALK
Definition Defines.php:73
const NS_MAIN
Definition Defines.php:64
const NS_PROJECT_TALK
Definition Defines.php:69
const NS_MEDIAWIKI
Definition Defines.php:72
const NS_TEMPLATE
Definition Defines.php:74
const NS_SPECIAL
Definition Defines.php:53
const NS_FILE_TALK
Definition Defines.php:71
const NS_HELP_TALK
Definition Defines.php:77
const NS_CATEGORY_TALK
Definition Defines.php:79
const NS_MEDIA
Definition Defines.php:52
const NS_TALK
Definition Defines.php:65
const NS_USER_TALK
Definition Defines.php:67
const NS_PROJECT
Definition Defines.php:68
const NS_CATEGORY
Definition Defines.php:78
const NS_TEMPLATE_TALK
Definition Defines.php:75
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition Setup.php:88
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? Note that this refers to structured handling of 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:69
getInterwiki()
The interwiki component of this LinkTarget.
getNamespace()
Get the namespace index.
getText()
Returns the link in text form, without namespace prefix or fragment.