Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.34% covered (success)
91.34%
116 / 127
94.12% covered (success)
94.12%
32 / 34
CRAP
0.00% covered (danger)
0.00%
0 / 1
NamespaceInfo
92.06% covered (success)
92.06%
116 / 126
94.12% covered (success)
94.12%
32 / 34
75.66
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 isMethodValidFor
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 makeValidNamespace
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 isMovable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 isSubject
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isTalk
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getTalk
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getTalkPage
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 canHaveTalkPage
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getSubject
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getSubjectPage
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getAssociated
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getAssociatedPage
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 exists
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 equals
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 subjectEquals
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCanonicalNamespaces
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 getCanonicalName
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCanonicalIndex
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getValidNamespaces
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 hasTalkNamespace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isContent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 wantSignatures
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isWatchable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasSubpages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentNamespaces
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getSubjectNamespaces
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getTalkNamespaces
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 isCapitalized
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 hasGenderDistinction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isNonincludable
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getNamespaceContentModel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryLinkType
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getCommonNamespaces
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Provide things related to namespaces.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23namespace MediaWiki\Title;
24
25use InvalidArgumentException;
26use MediaWiki\Config\ServiceOptions;
27use MediaWiki\HookContainer\HookContainer;
28use MediaWiki\HookContainer\HookRunner;
29use MediaWiki\Linker\LinkTarget;
30use MediaWiki\MainConfigNames;
31use MWException;
32
33/**
34 * This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of
35 * them based on index.  The textual names of the namespaces are handled by Language.php.
36 *
37 * @since 1.34
38 */
39class NamespaceInfo {
40
41    /**
42     * These namespaces should always be first-letter capitalized, now and
43     * forevermore. Historically, they could've probably been lowercased too,
44     * but some things are just too ingrained now. :)
45     */
46    private $alwaysCapitalizedNamespaces = [ NS_SPECIAL, NS_USER, NS_MEDIAWIKI ];
47
48    /** @var string[]|null Canonical namespaces cache */
49    private $canonicalNamespaces = null;
50
51    /** @var array|false Canonical namespaces index cache */
52    private $namespaceIndexes = false;
53
54    /** @var int[]|null Valid namespaces cache */
55    private $validNamespaces = null;
56
57    /** @var ServiceOptions */
58    private $options;
59
60    /** @var HookRunner */
61    private $hookRunner;
62
63    private array $extensionNamespaces;
64
65    private array $extensionImmovableNamespaces;
66
67    /**
68     * Definitions of the NS_ constants are in Defines.php
69     *
70     * @internal
71     */
72    public const CANONICAL_NAMES = [
73        NS_MEDIA            => 'Media',
74        NS_SPECIAL          => 'Special',
75        NS_MAIN             => '',
76        NS_TALK             => 'Talk',
77        NS_USER             => 'User',
78        NS_USER_TALK        => 'User_talk',
79        NS_PROJECT          => 'Project',
80        NS_PROJECT_TALK     => 'Project_talk',
81        NS_FILE             => 'File',
82        NS_FILE_TALK        => 'File_talk',
83        NS_MEDIAWIKI        => 'MediaWiki',
84        NS_MEDIAWIKI_TALK   => 'MediaWiki_talk',
85        NS_TEMPLATE         => 'Template',
86        NS_TEMPLATE_TALK    => 'Template_talk',
87        NS_HELP             => 'Help',
88        NS_HELP_TALK        => 'Help_talk',
89        NS_CATEGORY         => 'Category',
90        NS_CATEGORY_TALK    => 'Category_talk',
91    ];
92
93    /**
94     * @internal For use by ServiceWiring
95     */
96    public const CONSTRUCTOR_OPTIONS = [
97        MainConfigNames::CanonicalNamespaceNames,
98        MainConfigNames::CapitalLinkOverrides,
99        MainConfigNames::CapitalLinks,
100        MainConfigNames::ContentNamespaces,
101        MainConfigNames::ExtraNamespaces,
102        MainConfigNames::ExtraSignatureNamespaces,
103        MainConfigNames::NamespaceContentModels,
104        MainConfigNames::NamespacesWithSubpages,
105        MainConfigNames::NonincludableNamespaces,
106    ];
107
108    /**
109     * @param ServiceOptions $options
110     * @param HookContainer $hookContainer
111     * @param array $extensionNamespaces
112     * @param array $extensionImmovableNamespaces
113     */
114    public function __construct(
115        ServiceOptions $options,
116        HookContainer $hookContainer,
117        array $extensionNamespaces,
118        array $extensionImmovableNamespaces
119    ) {
120        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
121        $this->options = $options;
122        $this->hookRunner = new HookRunner( $hookContainer );
123        $this->extensionNamespaces = $extensionNamespaces;
124        $this->extensionImmovableNamespaces = $extensionImmovableNamespaces;
125    }
126
127    /**
128     * Throw an exception when trying to get the subject or talk page
129     * for a given namespace where it does not make sense.
130     * Special namespaces are defined in includes/Defines.php and have
131     * a value below 0 (ex: NS_SPECIAL = -1 , NS_MEDIA = -2)
132     *
133     * @param int $index
134     * @param string $method
135     *
136     * @throws MWException
137     * @return bool
138     */
139    private function isMethodValidFor( $index, $method ) {
140        if ( $index < NS_MAIN ) {
141            throw new MWException( "$method does not make any sense for given namespace $index" );
142        }
143        return true;
144    }
145
146    /**
147     * Throw if given index isn't an integer or integer-like string and so can't be a valid namespace.
148     *
149     * @param int|string $index
150     * @param string $method
151     *
152     * @throws InvalidArgumentException
153     * @return int Cleaned up namespace index
154     */
155    private function makeValidNamespace( $index, $method ) {
156        if ( !(
157            is_int( $index )
158            // Namespace index numbers as strings
159            || ctype_digit( $index )
160            // Negative numbers as strings
161            || ( $index[0] === '-' && ctype_digit( substr( $index, 1 ) ) )
162        ) ) {
163            throw new InvalidArgumentException(
164                "$method called with non-integer (" . gettype( $index ) . ") namespace '$index'"
165            );
166        }
167
168        return intval( $index );
169    }
170
171    /**
172     * Can pages in the given namespace be moved?
173     *
174     * @param int $index Namespace index
175     * @return bool
176     */
177    public function isMovable( $index ) {
178        $result = $index >= NS_MAIN && !in_array( $index, $this->extensionImmovableNamespaces );
179
180        /**
181         * @since 1.20
182         */
183        $this->hookRunner->onNamespaceIsMovable( $index, $result );
184
185        return $result;
186    }
187
188    /**
189     * Is the given namespace is a subject (non-talk) namespace?
190     *
191     * @param int $index Namespace index
192     * @return bool
193     */
194    public function isSubject( $index ) {
195        return !$this->isTalk( $index );
196    }
197
198    /**
199     * Is the given namespace a talk namespace?
200     *
201     * @param int $index Namespace index
202     * @return bool
203     */
204    public function isTalk( $index ) {
205        $index = $this->makeValidNamespace( $index, __METHOD__ );
206
207        return $index > NS_MAIN
208            && $index % 2 === 1;
209    }
210
211    /**
212     * Get the talk namespace index for a given namespace
213     *
214     * @param int $index Namespace index
215     * @return int
216     * @throws MWException if the given namespace doesn't have an associated talk namespace
217     *         (e.g. NS_SPECIAL).
218     */
219    public function getTalk( $index ) {
220        $index = $this->makeValidNamespace( $index, __METHOD__ );
221
222        $this->isMethodValidFor( $index, __METHOD__ );
223        return $this->isTalk( $index )
224            ? $index
225            : $index + 1;
226    }
227
228    /**
229     * Get a LinkTarget referring to the talk page of $target.
230     *
231     * @see canHaveTalkPage
232     * @param LinkTarget $target
233     * @return LinkTarget Talk page for $target
234     * @throws MWException if $target doesn't have talk pages, e.g. because it's in NS_SPECIAL,
235     *         because it's a relative section-only link, or it's an interwiki link.
236     */
237    public function getTalkPage( LinkTarget $target ): LinkTarget {
238        if ( $target->getText() === '' ) {
239            throw new MWException( 'Can\'t determine talk page associated with relative section link' );
240        }
241
242        if ( $target->getInterwiki() !== '' ) {
243            throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
244        }
245
246        if ( $this->isTalk( $target->getNamespace() ) ) {
247            return $target;
248        }
249
250        // NOTE: getTalk throws on bad namespaces!
251        return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
252    }
253
254    /**
255     * Can the title have a corresponding talk page?
256     *
257     * False for relative section-only links (with getText() === ''),
258     * interwiki links (with getInterwiki() !== ''), and pages in NS_SPECIAL.
259     *
260     * @see getTalkPage
261     *
262     * @param LinkTarget $target
263     * @return bool True if this title either is a talk page or can have a talk page associated.
264     */
265    public function canHaveTalkPage( LinkTarget $target ) {
266        return $target->getNamespace() >= NS_MAIN &&
267            !$target->isExternal() &&
268            $target->getText() !== '';
269    }
270
271    /**
272     * Get the subject namespace index for a given namespace
273     * Special namespaces (NS_MEDIA, NS_SPECIAL) are always the subject.
274     *
275     * @param int $index Namespace index
276     * @return int
277     */
278    public function getSubject( $index ) {
279        $index = $this->makeValidNamespace( $index, __METHOD__ );
280
281        # Handle special namespaces
282        if ( $index < NS_MAIN ) {
283            return $index;
284        }
285
286        return $this->isTalk( $index )
287            ? $index - 1
288            : $index;
289    }
290
291    /**
292     * @param LinkTarget $target
293     * @return LinkTarget Subject page for $target
294     */
295    public function getSubjectPage( LinkTarget $target ): LinkTarget {
296        if ( $this->isSubject( $target->getNamespace() ) ) {
297            return $target;
298        }
299        return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
300    }
301
302    /**
303     * Get the associated namespace.
304     * For talk namespaces, returns the subject (non-talk) namespace
305     * For subject (non-talk) namespaces, returns the talk namespace
306     *
307     * @param int $index Namespace index
308     * @return int
309     * @throws MWException if called on a namespace that has no talk pages (e.g., NS_SPECIAL)
310     */
311    public function getAssociated( $index ) {
312        $this->isMethodValidFor( $index, __METHOD__ );
313
314        if ( $this->isSubject( $index ) ) {
315            return $this->getTalk( $index );
316        }
317        return $this->getSubject( $index );
318    }
319
320    /**
321     * @param LinkTarget $target
322     * @return LinkTarget Talk page for $target if it's a subject page, subject page if it's a talk
323     *   page
324     * @throws MWException if $target's namespace doesn't have talk pages (e.g., NS_SPECIAL)
325     */
326    public function getAssociatedPage( LinkTarget $target ): LinkTarget {
327        if ( $target->getText() === '' ) {
328            throw new MWException( 'Can\'t determine talk page associated with relative section link' );
329        }
330
331        if ( $target->getInterwiki() !== '' ) {
332            throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
333        }
334
335        return new TitleValue(
336            $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
337    }
338
339    /**
340     * Returns whether the specified namespace exists
341     *
342     * @param int $index
343     *
344     * @return bool
345     */
346    public function exists( $index ) {
347        $nslist = $this->getCanonicalNamespaces();
348        return isset( $nslist[$index] );
349    }
350
351    /**
352     * Returns whether the specified namespaces are the same namespace
353     *
354     * @note It's possible that in the future we may start using something
355     * other than just namespace indexes. Under that circumstance making use
356     * of this function rather than directly doing comparison will make
357     * sure that code will not potentially break.
358     *
359     * @param int $ns1 The first namespace index
360     * @param int $ns2 The second namespace index
361     *
362     * @return bool
363     */
364    public function equals( $ns1, $ns2 ) {
365        return $ns1 == $ns2;
366    }
367
368    /**
369     * Returns whether the specified namespaces share the same subject.
370     * eg: NS_USER and NS_USER wil return true, as well
371     *     NS_USER and NS_USER_TALK will return true.
372     *
373     * @param int $ns1 The first namespace index
374     * @param int $ns2 The second namespace index
375     *
376     * @return bool
377     */
378    public function subjectEquals( $ns1, $ns2 ) {
379        return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
380    }
381
382    /**
383     * Returns array of all defined namespaces with their canonical
384     * (English) names.
385     *
386     * @return string[]
387     */
388    public function getCanonicalNamespaces() {
389        if ( $this->canonicalNamespaces === null ) {
390            $this->canonicalNamespaces =
391                [ NS_MAIN => '' ] + $this->options->get( MainConfigNames::CanonicalNamespaceNames );
392            $this->canonicalNamespaces += $this->extensionNamespaces;
393            if ( is_array( $this->options->get( MainConfigNames::ExtraNamespaces ) ) ) {
394                $this->canonicalNamespaces += $this->options->get( MainConfigNames::ExtraNamespaces );
395            }
396            $this->hookRunner->onCanonicalNamespaces( $this->canonicalNamespaces );
397        }
398        return $this->canonicalNamespaces;
399    }
400
401    /**
402     * Returns the canonical (English) name for a given index
403     *
404     * @param int $index Namespace index
405     * @return string|false If no canonical definition.
406     */
407    public function getCanonicalName( $index ) {
408        $nslist = $this->getCanonicalNamespaces();
409        return $nslist[$index] ?? false;
410    }
411
412    /**
413     * Returns the index for a given canonical name, or NULL
414     * The input *must* be converted to lower case first
415     *
416     * @param string $name Namespace name
417     * @return int|null
418     */
419    public function getCanonicalIndex( $name ) {
420        if ( $this->namespaceIndexes === false ) {
421            $this->namespaceIndexes = [];
422            foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
423                $this->namespaceIndexes[strtolower( $text )] = $i;
424            }
425        }
426        if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
427            return $this->namespaceIndexes[$name];
428        } else {
429            return null;
430        }
431    }
432
433    /**
434     * Returns an array of the namespaces (by integer id) that exist on the wiki. Used primarily by
435     * the API in help documentation. The array is sorted numerically and omits negative namespaces.
436     * @return array
437     */
438    public function getValidNamespaces() {
439        if ( $this->validNamespaces === null ) {
440            $this->validNamespaces = [];
441            foreach ( $this->getCanonicalNamespaces() as $ns => $_ ) {
442                if ( $ns >= 0 ) {
443                    $this->validNamespaces[] = $ns;
444                }
445            }
446            // T109137: sort numerically
447            sort( $this->validNamespaces, SORT_NUMERIC );
448        }
449
450        return $this->validNamespaces;
451    }
452
453    /**
454     * Does this namespace ever have a talk namespace?
455     *
456     * @param int $index Namespace ID
457     * @return bool True if this namespace either is or has a corresponding talk namespace.
458     */
459    public function hasTalkNamespace( $index ) {
460        return $index >= NS_MAIN;
461    }
462
463    /**
464     * Does this namespace contain content, for the purposes of calculating
465     * statistics, etc?
466     *
467     * @param int $index Index to check
468     * @return bool
469     */
470    public function isContent( $index ) {
471        return $index == NS_MAIN ||
472            in_array( $index, $this->options->get( MainConfigNames::ContentNamespaces ) );
473    }
474
475    /**
476     * Might pages in this namespace require the use of the Signature button on
477     * the edit toolbar?
478     *
479     * @param int $index Index to check
480     * @return bool
481     */
482    public function wantSignatures( $index ) {
483        return $this->isTalk( $index ) ||
484            in_array( $index, $this->options->get( MainConfigNames::ExtraSignatureNamespaces ) );
485    }
486
487    /**
488     * Can pages in a namespace be watched?
489     *
490     * @param int $index
491     * @return bool
492     */
493    public function isWatchable( $index ) {
494        return $index >= NS_MAIN;
495    }
496
497    /**
498     * Does the namespace allow subpages? Note that this refers to structured
499     * handling of subpages, and does not include SpecialPage subpage parameters.
500     *
501     * @param int $index Index to check
502     * @return bool
503     */
504    public function hasSubpages( $index ) {
505        return !empty( $this->options->get( MainConfigNames::NamespacesWithSubpages )[$index] );
506    }
507
508    /**
509     * Get a list of all namespace indices which are considered to contain content
510     * @return int[] Array of namespace indices
511     */
512    public function getContentNamespaces() {
513        $contentNamespaces = $this->options->get( MainConfigNames::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
524    /**
525     * List all namespace indices which are considered subject, aka not a talk
526     * or special namespace. See also NamespaceInfo::isSubject
527     *
528     * @return int[] Array of namespace indices
529     */
530    public function getSubjectNamespaces() {
531        return array_filter(
532            $this->getValidNamespaces(),
533            [ $this, 'isSubject' ]
534        );
535    }
536
537    /**
538     * List all namespace indices which are considered talks, aka not a subject
539     * or special namespace. See also NamespaceInfo::isTalk
540     *
541     * @return int[] Array of namespace indices
542     */
543    public function getTalkNamespaces() {
544        return array_filter(
545            $this->getValidNamespaces(),
546            [ $this, 'isTalk' ]
547        );
548    }
549
550    /**
551     * Is the namespace first-letter capitalized?
552     *
553     * @param int $index Index to check
554     * @return bool
555     */
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( MainConfigNames::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( MainConfigNames::CapitalLinks );
574    }
575
576    /**
577     * Does the namespace (potentially) have different aliases for different
578     * genders. Not all languages make a distinction here.
579     *
580     * @param int $index Index to check
581     * @return bool
582     */
583    public function hasGenderDistinction( $index ) {
584        return $index == NS_USER || $index == NS_USER_TALK;
585    }
586
587    /**
588     * It is not possible to use pages from this namespace as template?
589     *
590     * @param int $index Index to check
591     * @return bool
592     */
593    public function isNonincludable( $index ) {
594        $namespaces = $this->options->get( MainConfigNames::NonincludableNamespaces );
595        return $namespaces && in_array( $index, $namespaces );
596    }
597
598    /**
599     * Get the default content model for a namespace
600     * This does not mean that all pages in that namespace have the model
601     *
602     * @note To determine the default model for a new page's main slot, or any slot in general,
603     * use SlotRoleHandler::getDefaultModel() together with SlotRoleRegistry::getRoleHandler().
604     *
605     * @param int $index Index to check
606     * @return null|string Default model name for the given namespace, if set
607     */
608    public function getNamespaceContentModel( $index ) {
609        return $this->options->get( MainConfigNames::NamespaceContentModels )[$index] ?? null;
610    }
611
612    /**
613     * Returns the link type to be used for categories.
614     *
615     * This determines which section of a category page titles
616     * in the namespace will appear within.
617     *
618     * @param int $index Namespace index
619     * @return string One of 'subcat', 'file', 'page'
620     */
621    public function getCategoryLinkType( $index ) {
622        $this->isMethodValidFor( $index, __METHOD__ );
623
624        if ( $index == NS_CATEGORY ) {
625            return 'subcat';
626        } elseif ( $index == NS_FILE ) {
627            return 'file';
628        } else {
629            return 'page';
630        }
631    }
632
633    /**
634     * Retrieve the indexes for the namespaces defined by core.
635     *
636     * @since 1.34
637     *
638     * @return int[]
639     */
640    public static function getCommonNamespaces() {
641        return array_keys( self::CANONICAL_NAMES );
642    }
643}
644
645/** @deprecated class alias since 1.41 */
646class_alias( NamespaceInfo::class, 'NamespaceInfo' );