MediaWiki REL1_39
NamespaceInfo.php
Go to the documentation of this file.
1<?php
29
37
43 private $alwaysCapitalizedNamespaces = [ NS_SPECIAL, NS_USER, NS_MEDIAWIKI ];
44
46 private $canonicalNamespaces = null;
47
49 private $namespaceIndexes = false;
50
52 private $validNamespaces = null;
53
55 private $options;
56
58 private $hookRunner;
59
65 public const CANONICAL_NAMES = [
66 NS_MEDIA => 'Media',
67 NS_SPECIAL => 'Special',
68 NS_MAIN => '',
69 NS_TALK => 'Talk',
70 NS_USER => 'User',
71 NS_USER_TALK => 'User_talk',
72 NS_PROJECT => 'Project',
73 NS_PROJECT_TALK => 'Project_talk',
74 NS_FILE => 'File',
75 NS_FILE_TALK => 'File_talk',
76 NS_MEDIAWIKI => 'MediaWiki',
77 NS_MEDIAWIKI_TALK => 'MediaWiki_talk',
78 NS_TEMPLATE => 'Template',
79 NS_TEMPLATE_TALK => 'Template_talk',
80 NS_HELP => 'Help',
81 NS_HELP_TALK => 'Help_talk',
82 NS_CATEGORY => 'Category',
83 NS_CATEGORY_TALK => 'Category_talk',
84 ];
85
89 public const CONSTRUCTOR_OPTIONS = [
90 MainConfigNames::CanonicalNamespaceNames,
91 MainConfigNames::CapitalLinkOverrides,
92 MainConfigNames::CapitalLinks,
93 MainConfigNames::ContentNamespaces,
94 MainConfigNames::ExtraNamespaces,
95 MainConfigNames::ExtraSignatureNamespaces,
96 MainConfigNames::NamespaceContentModels,
97 MainConfigNames::NamespacesWithSubpages,
98 MainConfigNames::NonincludableNamespaces,
99 ];
100
105 public function __construct( ServiceOptions $options, HookContainer $hookContainer ) {
106 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
107 $this->options = $options;
108 $this->hookRunner = new HookRunner( $hookContainer );
109 }
110
123 private function isMethodValidFor( $index, $method ) {
124 if ( $index < NS_MAIN ) {
125 throw new MWException( "$method does not make any sense for given namespace $index" );
126 }
127 return true;
128 }
129
139 private function makeValidNamespace( $index, $method ) {
140 if ( !(
141 is_int( $index )
142 // Namespace index numbers as strings
143 || ctype_digit( $index )
144 // Negative numbers as strings
145 || ( $index[0] === '-' && ctype_digit( substr( $index, 1 ) ) )
146 ) ) {
147 throw new InvalidArgumentException(
148 "$method called with non-integer (" . gettype( $index ) . ") namespace '$index'"
149 );
150 }
151
152 return intval( $index );
153 }
154
161 public function isMovable( $index ) {
162 $extensionRegistry = ExtensionRegistry::getInstance();
163 $extNamespaces = $extensionRegistry->getAttribute( 'ImmovableNamespaces' );
164
165 $result = $index >= NS_MAIN && !in_array( $index, $extNamespaces );
166
170 $this->hookRunner->onNamespaceIsMovable( $index, $result );
171
172 return $result;
173 }
174
181 public function isSubject( $index ) {
182 return !$this->isTalk( $index );
183 }
184
191 public function isTalk( $index ) {
192 $index = $this->makeValidNamespace( $index, __METHOD__ );
193
194 return $index > NS_MAIN
195 && $index % 2;
196 }
197
206 public function getTalk( $index ) {
207 $index = $this->makeValidNamespace( $index, __METHOD__ );
208
209 $this->isMethodValidFor( $index, __METHOD__ );
210 return $this->isTalk( $index )
211 ? $index
212 : $index + 1;
213 }
214
224 public function getTalkPage( LinkTarget $target ): LinkTarget {
225 if ( $target->getText() === '' ) {
226 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
227 }
228
229 if ( $target->getInterwiki() !== '' ) {
230 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
231 }
232
233 if ( $this->isTalk( $target->getNamespace() ) ) {
234 return $target;
235 }
236
237 // NOTE: getTalk throws on bad namespaces!
238 return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
239 }
240
252 public function canHaveTalkPage( LinkTarget $target ) {
253 if ( $target->getText() === '' || $target->getInterwiki() !== '' ) {
254 return false;
255 }
256
257 if ( $target->getNamespace() < NS_MAIN ) {
258 return false;
259 }
260
261 return true;
262 }
263
271 public function getSubject( $index ) {
272 $index = $this->makeValidNamespace( $index, __METHOD__ );
273
274 # Handle special namespaces
275 if ( $index < NS_MAIN ) {
276 return $index;
277 }
278
279 return $this->isTalk( $index )
280 ? $index - 1
281 : $index;
282 }
283
288 public function getSubjectPage( LinkTarget $target ): LinkTarget {
289 if ( $this->isSubject( $target->getNamespace() ) ) {
290 return $target;
291 }
292 return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
293 }
294
304 public function getAssociated( $index ) {
305 $this->isMethodValidFor( $index, __METHOD__ );
306
307 if ( $this->isSubject( $index ) ) {
308 return $this->getTalk( $index );
309 }
310 return $this->getSubject( $index );
311 }
312
319 public function getAssociatedPage( LinkTarget $target ): LinkTarget {
320 if ( $target->getText() === '' ) {
321 throw new MWException( 'Can\'t determine talk page associated with relative section link' );
322 }
323
324 if ( $target->getInterwiki() !== '' ) {
325 throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
326 }
327
328 return new TitleValue(
329 $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
330 }
331
339 public function exists( $index ) {
340 $nslist = $this->getCanonicalNamespaces();
341 return isset( $nslist[$index] );
342 }
343
357 public function equals( $ns1, $ns2 ) {
358 return $ns1 == $ns2;
359 }
360
371 public function subjectEquals( $ns1, $ns2 ) {
372 return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
373 }
374
381 public function getCanonicalNamespaces() {
382 if ( $this->canonicalNamespaces === null ) {
383 $this->canonicalNamespaces =
384 [ NS_MAIN => '' ] + $this->options->get( MainConfigNames::CanonicalNamespaceNames );
385 $this->canonicalNamespaces +=
386 ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
387 if ( is_array( $this->options->get( MainConfigNames::ExtraNamespaces ) ) ) {
388 $this->canonicalNamespaces += $this->options->get( MainConfigNames::ExtraNamespaces );
389 }
390 $this->hookRunner->onCanonicalNamespaces( $this->canonicalNamespaces );
391 }
392 return $this->canonicalNamespaces;
393 }
394
401 public function getCanonicalName( $index ) {
402 $nslist = $this->getCanonicalNamespaces();
403 return $nslist[$index] ?? false;
404 }
405
413 public function getCanonicalIndex( $name ) {
414 if ( $this->namespaceIndexes === false ) {
415 $this->namespaceIndexes = [];
416 foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
417 $this->namespaceIndexes[strtolower( $text )] = $i;
418 }
419 }
420 if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
421 return $this->namespaceIndexes[$name];
422 } else {
423 return null;
424 }
425 }
426
432 public function getValidNamespaces() {
433 if ( $this->validNamespaces === null ) {
434 $this->validNamespaces = [];
435 foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
436 if ( $ns >= 0 ) {
437 $this->validNamespaces[] = $ns;
438 }
439 }
440 // T109137: sort numerically
441 sort( $this->validNamespaces, SORT_NUMERIC );
442 }
443
444 return $this->validNamespaces;
445 }
446
453 public function hasTalkNamespace( $index ) {
454 return $index >= NS_MAIN;
455 }
456
464 public function isContent( $index ) {
465 return $index == NS_MAIN ||
466 in_array( $index, $this->options->get( MainConfigNames::ContentNamespaces ) );
467 }
468
476 public function wantSignatures( $index ) {
477 return $this->isTalk( $index ) ||
478 in_array( $index, $this->options->get( MainConfigNames::ExtraSignatureNamespaces ) );
479 }
480
487 public function isWatchable( $index ) {
488 return $index >= NS_MAIN;
489 }
490
498 public function hasSubpages( $index ) {
499 return !empty( $this->options->get( MainConfigNames::NamespacesWithSubpages )[$index] );
500 }
501
506 public function getContentNamespaces() {
507 $contentNamespaces = $this->options->get( MainConfigNames::ContentNamespaces );
508 if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
509 return [ NS_MAIN ];
510 } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
511 // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
512 return array_merge( [ NS_MAIN ], $contentNamespaces );
513 } else {
514 return $contentNamespaces;
515 }
516 }
517
524 public function getSubjectNamespaces() {
525 return array_filter(
526 $this->getValidNamespaces(),
527 [ $this, 'isSubject' ]
528 );
529 }
530
537 public function getTalkNamespaces() {
538 return array_filter(
539 $this->getValidNamespaces(),
540 [ $this, 'isTalk' ]
541 );
542 }
543
550 public function isCapitalized( $index ) {
551 // Turn NS_MEDIA into NS_FILE
552 $index = $index === NS_MEDIA ? NS_FILE : $index;
553
554 // Make sure to get the subject of our namespace
555 $index = $this->getSubject( $index );
556
557 // Some namespaces are special and should always be upper case
558 if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
559 return true;
560 }
561 $overrides = $this->options->get( MainConfigNames::CapitalLinkOverrides );
562 if ( isset( $overrides[$index] ) ) {
563 // CapitalLinkOverrides is explicitly set
564 return $overrides[$index];
565 }
566 // Default to the global setting
567 return $this->options->get( MainConfigNames::CapitalLinks );
568 }
569
577 public function hasGenderDistinction( $index ) {
578 return $index == NS_USER || $index == NS_USER_TALK;
579 }
580
587 public function isNonincludable( $index ) {
588 $namespaces = $this->options->get( MainConfigNames::NonincludableNamespaces );
589 return $namespaces && in_array( $index, $namespaces );
590 }
591
602 public function getNamespaceContentModel( $index ) {
603 return $this->options->get( MainConfigNames::NamespaceContentModels )[$index] ?? null;
604 }
605
615 public function getRestrictionLevels( $index, User $user = null ) {
616 // PermissionManager is not injected because adding an explicit dependency
617 // breaks MW installer by adding a dependency chain on the database before
618 // it was set up. Also, the method is deprecated and will be soon removed.
619 wfDeprecated( __METHOD__, '1.34' );
620 return MediaWikiServices::getInstance()
621 ->getPermissionManager()
622 ->getNamespaceRestrictionLevels( $index, $user );
623 }
624
634 public function getCategoryLinkType( $index ) {
635 $this->isMethodValidFor( $index, __METHOD__ );
636
637 if ( $index == NS_CATEGORY ) {
638 return 'subcat';
639 } elseif ( $index == NS_FILE ) {
640 return 'file';
641 } else {
642 return 'page';
643 }
644 }
645
653 public static function getCommonNamespaces() {
654 return array_keys( self::CANONICAL_NAMES );
655 }
656}
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(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
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...
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
getAssociatedPage(LinkTarget $target)
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.
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...
hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?
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)
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,...
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.
internal since 1.36
Definition User.php:70
getInterwiki()
The interwiki component of this LinkTarget.
getNamespace()
Get the namespace index.
getText()
Get the main part of the link target, in text form.