Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
72.06% covered (warning)
72.06%
196 / 272
28.57% covered (danger)
28.57%
4 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialPageFactory
72.06% covered (warning)
72.06%
196 / 272
28.57% covered (danger)
28.57%
4 / 14
193.45
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getNames
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPageList
67.57% covered (warning)
67.57%
50 / 74
0.00% covered (danger)
0.00%
0 / 1
11.76
 getAliasList
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
11
 resolveAlias
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
2.01
 exists
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPage
85.00% covered (warning)
85.00%
17 / 20
0.00% covered (danger)
0.00%
0 / 1
6.12
 getUsablePages
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 getRegularPages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 getListedPages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 executePath
58.33% covered (warning)
58.33%
21 / 36
0.00% covered (danger)
0.00%
0 / 1
22.42
 capturePath
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
1 / 1
3
 getLocalNameFor
70.37% covered (warning)
70.37%
19 / 27
0.00% covered (danger)
0.00%
0 / 1
14.15
 getTitleForAlias
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
1<?php
2/**
3 * Factory for handling the special page list and generating SpecialPage objects.
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 * @ingroup SpecialPage
22 * @defgroup SpecialPage SpecialPage
23 */
24
25namespace MediaWiki\SpecialPage;
26
27use Language;
28use MediaWiki\Config\ServiceOptions;
29use MediaWiki\Context\IContextSource;
30use MediaWiki\Context\RequestContext;
31use MediaWiki\HookContainer\HookContainer;
32use MediaWiki\HookContainer\HookRunner;
33use MediaWiki\Linker\LinkRenderer;
34use MediaWiki\MainConfigNames;
35use MediaWiki\Page\PageReference;
36use MediaWiki\Profiler\ProfilingContext;
37use MediaWiki\Specials\Redirects\SpecialAllMyUploads;
38use MediaWiki\Specials\Redirects\SpecialListAdmins;
39use MediaWiki\Specials\Redirects\SpecialListBots;
40use MediaWiki\Specials\Redirects\SpecialMycontributions;
41use MediaWiki\Specials\Redirects\SpecialMylog;
42use MediaWiki\Specials\Redirects\SpecialMypage;
43use MediaWiki\Specials\Redirects\SpecialMytalk;
44use MediaWiki\Specials\Redirects\SpecialMyuploads;
45use MediaWiki\Specials\Redirects\SpecialTalkPage;
46use MediaWiki\Specials\SpecialActiveUsers;
47use MediaWiki\Specials\SpecialAllMessages;
48use MediaWiki\Specials\SpecialAllPages;
49use MediaWiki\Specials\SpecialAncientPages;
50use MediaWiki\Specials\SpecialApiHelp;
51use MediaWiki\Specials\SpecialApiSandbox;
52use MediaWiki\Specials\SpecialAuthenticationPopupSuccess;
53use MediaWiki\Specials\SpecialAutoblockList;
54use MediaWiki\Specials\SpecialBlankpage;
55use MediaWiki\Specials\SpecialBlock;
56use MediaWiki\Specials\SpecialBlockList;
57use MediaWiki\Specials\SpecialBookSources;
58use MediaWiki\Specials\SpecialBotPasswords;
59use MediaWiki\Specials\SpecialBrokenRedirects;
60use MediaWiki\Specials\SpecialCategories;
61use MediaWiki\Specials\SpecialChangeContentModel;
62use MediaWiki\Specials\SpecialChangeCredentials;
63use MediaWiki\Specials\SpecialChangeEmail;
64use MediaWiki\Specials\SpecialChangePassword;
65use MediaWiki\Specials\SpecialComparePages;
66use MediaWiki\Specials\SpecialConfirmEmail;
67use MediaWiki\Specials\SpecialContribute;
68use MediaWiki\Specials\SpecialContributions;
69use MediaWiki\Specials\SpecialCreateAccount;
70use MediaWiki\Specials\SpecialDeadendPages;
71use MediaWiki\Specials\SpecialDeletedContributions;
72use MediaWiki\Specials\SpecialDeletePage;
73use MediaWiki\Specials\SpecialDiff;
74use MediaWiki\Specials\SpecialDoubleRedirects;
75use MediaWiki\Specials\SpecialEditPage;
76use MediaWiki\Specials\SpecialEditRecovery;
77use MediaWiki\Specials\SpecialEditTags;
78use MediaWiki\Specials\SpecialEditWatchlist;
79use MediaWiki\Specials\SpecialEmailInvalidate;
80use MediaWiki\Specials\SpecialEmailUser;
81use MediaWiki\Specials\SpecialExpandTemplates;
82use MediaWiki\Specials\SpecialExport;
83use MediaWiki\Specials\SpecialFewestRevisions;
84use MediaWiki\Specials\SpecialFileDuplicateSearch;
85use MediaWiki\Specials\SpecialFilepath;
86use MediaWiki\Specials\SpecialGoToInterwiki;
87use MediaWiki\Specials\SpecialImport;
88use MediaWiki\Specials\SpecialJavaScriptTest;
89use MediaWiki\Specials\SpecialLinkAccounts;
90use MediaWiki\Specials\SpecialLinkSearch;
91use MediaWiki\Specials\SpecialListDuplicatedFiles;
92use MediaWiki\Specials\SpecialListFiles;
93use MediaWiki\Specials\SpecialListGrants;
94use MediaWiki\Specials\SpecialListGroupRights;
95use MediaWiki\Specials\SpecialListRedirects;
96use MediaWiki\Specials\SpecialListUsers;
97use MediaWiki\Specials\SpecialLockdb;
98use MediaWiki\Specials\SpecialLog;
99use MediaWiki\Specials\SpecialLonelyPages;
100use MediaWiki\Specials\SpecialLongPages;
101use MediaWiki\Specials\SpecialMediaStatistics;
102use MediaWiki\Specials\SpecialMergeHistory;
103use MediaWiki\Specials\SpecialMIMESearch;
104use MediaWiki\Specials\SpecialMostCategories;
105use MediaWiki\Specials\SpecialMostImages;
106use MediaWiki\Specials\SpecialMostInterwikis;
107use MediaWiki\Specials\SpecialMostLinked;
108use MediaWiki\Specials\SpecialMostLinkedCategories;
109use MediaWiki\Specials\SpecialMostLinkedTemplates;
110use MediaWiki\Specials\SpecialMostRevisions;
111use MediaWiki\Specials\SpecialMovePage;
112use MediaWiki\Specials\SpecialMute;
113use MediaWiki\Specials\SpecialMyLanguage;
114use MediaWiki\Specials\SpecialNewFiles;
115use MediaWiki\Specials\SpecialNewPages;
116use MediaWiki\Specials\SpecialNewSection;
117use MediaWiki\Specials\SpecialPageData;
118use MediaWiki\Specials\SpecialPageHistory;
119use MediaWiki\Specials\SpecialPageInfo;
120use MediaWiki\Specials\SpecialPageLanguage;
121use MediaWiki\Specials\SpecialPagesWithProp;
122use MediaWiki\Specials\SpecialPasswordPolicies;
123use MediaWiki\Specials\SpecialPasswordReset;
124use MediaWiki\Specials\SpecialPermanentLink;
125use MediaWiki\Specials\SpecialPreferences;
126use MediaWiki\Specials\SpecialPrefixIndex;
127use MediaWiki\Specials\SpecialProtectedPages;
128use MediaWiki\Specials\SpecialProtectedTitles;
129use MediaWiki\Specials\SpecialProtectPage;
130use MediaWiki\Specials\SpecialPurge;
131use MediaWiki\Specials\SpecialRandomInCategory;
132use MediaWiki\Specials\SpecialRandomPage;
133use MediaWiki\Specials\SpecialRandomRedirect;
134use MediaWiki\Specials\SpecialRandomRootPage;
135use MediaWiki\Specials\SpecialRecentChanges;
136use MediaWiki\Specials\SpecialRecentChangesLinked;
137use MediaWiki\Specials\SpecialRedirect;
138use MediaWiki\Specials\SpecialRemoveCredentials;
139use MediaWiki\Specials\SpecialRenameUser;
140use MediaWiki\Specials\SpecialResetTokens;
141use MediaWiki\Specials\SpecialRestSandbox;
142use MediaWiki\Specials\SpecialRevisionDelete;
143use MediaWiki\Specials\SpecialRunJobs;
144use MediaWiki\Specials\SpecialSearch;
145use MediaWiki\Specials\SpecialShortPages;
146use MediaWiki\Specials\SpecialSpecialPages;
147use MediaWiki\Specials\SpecialStatistics;
148use MediaWiki\Specials\SpecialTags;
149use MediaWiki\Specials\SpecialTrackingCategories;
150use MediaWiki\Specials\SpecialUnblock;
151use MediaWiki\Specials\SpecialUncategorizedCategories;
152use MediaWiki\Specials\SpecialUncategorizedImages;
153use MediaWiki\Specials\SpecialUncategorizedPages;
154use MediaWiki\Specials\SpecialUncategorizedTemplates;
155use MediaWiki\Specials\SpecialUndelete;
156use MediaWiki\Specials\SpecialUnlinkAccounts;
157use MediaWiki\Specials\SpecialUnlockdb;
158use MediaWiki\Specials\SpecialUnusedCategories;
159use MediaWiki\Specials\SpecialUnusedImages;
160use MediaWiki\Specials\SpecialUnusedTemplates;
161use MediaWiki\Specials\SpecialUnwatchedPages;
162use MediaWiki\Specials\SpecialUpload;
163use MediaWiki\Specials\SpecialUploadStash;
164use MediaWiki\Specials\SpecialUserLogin;
165use MediaWiki\Specials\SpecialUserLogout;
166use MediaWiki\Specials\SpecialUserRights;
167use MediaWiki\Specials\SpecialVersion;
168use MediaWiki\Specials\SpecialWantedCategories;
169use MediaWiki\Specials\SpecialWantedFiles;
170use MediaWiki\Specials\SpecialWantedPages;
171use MediaWiki\Specials\SpecialWantedTemplates;
172use MediaWiki\Specials\SpecialWatchlist;
173use MediaWiki\Specials\SpecialWhatLinksHere;
174use MediaWiki\Specials\SpecialWithoutInterwiki;
175use MediaWiki\Title\Title;
176use MediaWiki\Title\TitleFactory;
177use MediaWiki\User\User;
178use Profiler;
179use Wikimedia\DebugInfo\DebugInfoTrait;
180use Wikimedia\ObjectFactory\ObjectFactory;
181
182/**
183 * Factory for handling the special page list and generating SpecialPage objects.
184 *
185 * To add a special page in an extension, add to $wgSpecialPages either
186 * an object instance or an array containing the name and constructor
187 * parameters. The latter is preferred for performance reasons.
188 *
189 * The object instantiated must be either an instance of SpecialPage or a
190 * sub-class thereof. It must have an execute() method, which sends the HTML
191 * for the special page to $wgOut. The parent class has an execute() method
192 * which distributes the call to the historical global functions. Additionally,
193 * execute() also checks if the user has the necessary access privileges
194 * and bails out if not.
195 *
196 * To add a core special page, use the similar static list in
197 * SpecialPageFactory::$list. To remove a core static special page at runtime, use
198 * a SpecialPage_initList hook.
199 *
200 * @ingroup SpecialPage
201 * @since 1.17
202 */
203class SpecialPageFactory {
204    use DebugInfoTrait;
205
206    /**
207     * List of special page names to the subclass of SpecialPage which handles them.
208     */
209    private const CORE_LIST = [
210        // Maintenance Reports
211        'BrokenRedirects' => [
212            'class' => SpecialBrokenRedirects::class,
213            'services' => [
214                'ContentHandlerFactory',
215                'ConnectionProvider',
216                'LinkBatchFactory',
217            ]
218        ],
219        'Deadendpages' => [
220            'class' => SpecialDeadendPages::class,
221            'services' => [
222                'NamespaceInfo',
223                'ConnectionProvider',
224                'LinkBatchFactory',
225                'LanguageConverterFactory',
226            ]
227        ],
228        'DoubleRedirects' => [
229            'class' => SpecialDoubleRedirects::class,
230            'services' => [
231                'ContentHandlerFactory',
232                'LinkBatchFactory',
233                'ConnectionProvider',
234            ]
235        ],
236        'Longpages' => [
237            'class' => SpecialLongPages::class,
238            'services' => [
239                // Same as for Shortpages
240                'NamespaceInfo',
241                'ConnectionProvider',
242                'LinkBatchFactory',
243            ]
244        ],
245        'Ancientpages' => [
246            'class' => SpecialAncientPages::class,
247            'services' => [
248                'NamespaceInfo',
249                'ConnectionProvider',
250                'LinkBatchFactory',
251                'LanguageConverterFactory',
252            ]
253        ],
254        'Lonelypages' => [
255            'class' => SpecialLonelyPages::class,
256            'services' => [
257                'NamespaceInfo',
258                'ConnectionProvider',
259                'LinkBatchFactory',
260                'LanguageConverterFactory',
261                'LinksMigration',
262            ]
263        ],
264        'Fewestrevisions' => [
265            'class' => SpecialFewestRevisions::class,
266            'services' => [
267                // Same as for Mostrevisions
268                'NamespaceInfo',
269                'ConnectionProvider',
270                'LinkBatchFactory',
271                'LanguageConverterFactory',
272            ]
273        ],
274        'Withoutinterwiki' => [
275            'class' => SpecialWithoutInterwiki::class,
276            'services' => [
277                'NamespaceInfo',
278                'ConnectionProvider',
279                'LinkBatchFactory',
280                'LanguageConverterFactory',
281            ]
282        ],
283        'Protectedpages' => [
284            'class' => SpecialProtectedPages::class,
285            'services' => [
286                'LinkBatchFactory',
287                'ConnectionProvider',
288                'CommentStore',
289                'RowCommentFormatter',
290                'RestrictionStore',
291            ]
292        ],
293        'Protectedtitles' => [
294            'class' => SpecialProtectedTitles::class,
295            'services' => [
296                'LinkBatchFactory',
297                'ConnectionProvider',
298            ]
299        ],
300        'Shortpages' => [
301            'class' => SpecialShortPages::class,
302            'services' => [
303                // Same as for Longpages
304                'NamespaceInfo',
305                'ConnectionProvider',
306                'LinkBatchFactory',
307            ]
308        ],
309        'Uncategorizedcategories' => [
310            'class' => SpecialUncategorizedCategories::class,
311            'services' => [
312                // Same as for SpecialUncategorizedPages and SpecialUncategorizedTemplates
313                'NamespaceInfo',
314                'ConnectionProvider',
315                'LinkBatchFactory',
316                'LanguageConverterFactory',
317            ]
318        ],
319        'Uncategorizedimages' => [
320            'class' => SpecialUncategorizedImages::class,
321            'services' => [
322                'ConnectionProvider',
323            ]
324        ],
325        'Uncategorizedpages' => [
326            'class' => SpecialUncategorizedPages::class,
327            'services' => [
328                // Same as for SpecialUncategorizedCategories and SpecialUncategorizedTemplates
329                'NamespaceInfo',
330                'ConnectionProvider',
331                'LinkBatchFactory',
332                'LanguageConverterFactory',
333            ]
334        ],
335        'Uncategorizedtemplates' => [
336            'class' => SpecialUncategorizedTemplates::class,
337            'services' => [
338                // Same as for SpecialUncategorizedCategories and SpecialUncategorizedPages
339                'NamespaceInfo',
340                'ConnectionProvider',
341                'LinkBatchFactory',
342                'LanguageConverterFactory',
343            ]
344        ],
345        'Unusedcategories' => [
346            'class' => SpecialUnusedCategories::class,
347            'services' => [
348                'ConnectionProvider',
349                'LinkBatchFactory',
350            ]
351        ],
352        'Unusedimages' => [
353            'class' => SpecialUnusedImages::class,
354            'services' => [
355                'ConnectionProvider',
356            ]
357        ],
358        'Unusedtemplates' => [
359            'class' => SpecialUnusedTemplates::class,
360            'services' => [
361                'ConnectionProvider',
362                'LinksMigration',
363            ]
364        ],
365        'Unwatchedpages' => [
366            'class' => SpecialUnwatchedPages::class,
367            'services' => [
368                'LinkBatchFactory',
369                'ConnectionProvider',
370                'LanguageConverterFactory',
371            ]
372        ],
373        'Wantedcategories' => [
374            'class' => SpecialWantedCategories::class,
375            'services' => [
376                'ConnectionProvider',
377                'LinkBatchFactory',
378                'LanguageConverterFactory',
379            ]
380        ],
381        'Wantedfiles' => [
382            'class' => SpecialWantedFiles::class,
383            'services' => [
384                'RepoGroup',
385                'ConnectionProvider',
386                'LinkBatchFactory',
387            ]
388        ],
389        'Wantedpages' => [
390            'class' => SpecialWantedPages::class,
391            'services' => [
392                'ConnectionProvider',
393                'LinkBatchFactory',
394                'LinksMigration',
395            ]
396        ],
397        'Wantedtemplates' => [
398            'class' => SpecialWantedTemplates::class,
399            'services' => [
400                'ConnectionProvider',
401                'LinkBatchFactory',
402                'LinksMigration',
403            ]
404        ],
405
406        // List of pages
407        'Allpages' => [
408            'class' => SpecialAllPages::class,
409            'services' => [
410                'ConnectionProvider',
411                'SearchEngineFactory',
412                'PageStore',
413            ]
414        ],
415        'Prefixindex' => [
416            'class' => SpecialPrefixIndex::class,
417            'services' => [
418                'ConnectionProvider',
419                'LinkCache',
420            ]
421        ],
422        'Categories' => [
423            'class' => SpecialCategories::class,
424            'services' => [
425                'LinkBatchFactory',
426                'ConnectionProvider',
427            ]
428        ],
429        'Listredirects' => [
430            'class' => SpecialListRedirects::class,
431            'services' => [
432                'LinkBatchFactory',
433                'ConnectionProvider',
434                'WikiPageFactory',
435                'RedirectLookup'
436            ]
437        ],
438        'PagesWithProp' => [
439            'class' => SpecialPagesWithProp::class,
440            'services' => [
441                'ConnectionProvider',
442            ]
443        ],
444        'TrackingCategories' => [
445            'class' => SpecialTrackingCategories::class,
446            'services' => [
447                'LinkBatchFactory',
448                'TrackingCategories',
449            ]
450        ],
451
452        // Authentication
453        'Userlogin' => [
454            'class' => SpecialUserLogin::class,
455            'services' => [
456                'AuthManager',
457            ]
458        ],
459        'Userlogout' => [
460            'class' => SpecialUserLogout::class,
461        ],
462        'CreateAccount' => [
463            'class' => SpecialCreateAccount::class,
464            'services' => [
465                'AuthManager',
466                'FormatterFactory',
467            ]
468        ],
469        'LinkAccounts' => [
470            'class' => SpecialLinkAccounts::class,
471            'services' => [
472                'AuthManager',
473            ]
474        ],
475        'UnlinkAccounts' => [
476            'class' => SpecialUnlinkAccounts::class,
477            'services' => [
478                'AuthManager',
479            ]
480        ],
481        'ChangeCredentials' => [
482            'class' => SpecialChangeCredentials::class,
483            'services' => [
484                'AuthManager',
485            ]
486        ],
487        'RemoveCredentials' => [
488            'class' => SpecialRemoveCredentials::class,
489            'services' => [
490                'AuthManager',
491            ]
492        ],
493        'AuthenticationPopupSuccess' => [
494            'class' => SpecialAuthenticationPopupSuccess::class,
495            'services' => [
496                'SkinFactory',
497            ]
498        ],
499
500        // Users and rights
501        'Activeusers' => [
502            'class' => SpecialActiveUsers::class,
503            'services' => [
504                'LinkBatchFactory',
505                'ConnectionProvider',
506                'UserGroupManager',
507                'UserIdentityLookup',
508                'HideUserUtils',
509            ]
510        ],
511        'Block' => [
512            'class' => SpecialBlock::class,
513            'services' => [
514                'BlockUtils',
515                'BlockPermissionCheckerFactory',
516                'BlockUserFactory',
517                'DatabaseBlockStore',
518                'UserNameUtils',
519                'UserNamePrefixSearch',
520                'BlockActionInfo',
521                'TitleFormatter',
522                'NamespaceInfo'
523            ]
524        ],
525        'Unblock' => [
526            'class' => SpecialUnblock::class,
527            'services' => [
528                'UnblockUserFactory',
529                'BlockUtils',
530                'DatabaseBlockStore',
531                'UserNameUtils',
532                'UserNamePrefixSearch',
533                'WatchlistManager',
534            ]
535        ],
536        'BlockList' => [
537            'class' => SpecialBlockList::class,
538            'services' => [
539                'LinkBatchFactory',
540                'DatabaseBlockStore',
541                'BlockRestrictionStore',
542                'ConnectionProvider',
543                'CommentStore',
544                'BlockUtils',
545                'HideUserUtils',
546                'BlockActionInfo',
547                'RowCommentFormatter',
548            ],
549        ],
550        'AutoblockList' => [
551            'class' => SpecialAutoblockList::class,
552            'services' => [
553                'LinkBatchFactory',
554                'BlockRestrictionStore',
555                'ConnectionProvider',
556                'CommentStore',
557                'BlockUtils',
558                'HideUserUtils',
559                'BlockActionInfo',
560                'RowCommentFormatter',
561            ],
562        ],
563        'ChangePassword' => [
564            'class' => SpecialChangePassword::class,
565        ],
566        'BotPasswords' => [
567            'class' => SpecialBotPasswords::class,
568            'services' => [
569                'PasswordFactory',
570                'AuthManager',
571                'CentralIdLookup',
572                'GrantsInfo',
573                'GrantsLocalization',
574            ]
575        ],
576        'PasswordReset' => [
577            'class' => SpecialPasswordReset::class,
578            'services' => [
579                'PasswordReset'
580            ]
581        ],
582        'DeletedContributions' => [
583            'class' => SpecialDeletedContributions::class,
584            'services' => [
585                'PermissionManager',
586                'ConnectionProvider',
587                'RevisionFactory',
588                'NamespaceInfo',
589                'UserFactory',
590                'UserNameUtils',
591                'UserNamePrefixSearch',
592                'CommentFormatter',
593                'LinkBatchFactory',
594                'DatabaseBlockStore',
595            ]
596        ],
597        'Preferences' => [
598            'class' => SpecialPreferences::class,
599            'services' => [
600                'PreferencesFactory',
601                'UserOptionsManager',
602            ]
603        ],
604        'ResetTokens' => [
605            'class' => SpecialResetTokens::class,
606        ],
607        'Contributions' => [
608            'class' => SpecialContributions::class,
609            'services' => [
610                'LinkBatchFactory',
611                'PermissionManager',
612                'ConnectionProvider',
613                'RevisionStore',
614                'NamespaceInfo',
615                'UserNameUtils',
616                'UserNamePrefixSearch',
617                'UserOptionsLookup',
618                'CommentFormatter',
619                'UserFactory',
620                'UserIdentityLookup',
621                'DatabaseBlockStore',
622            ]
623        ],
624        'Listgrouprights' => [
625            'class' => SpecialListGroupRights::class,
626            'services' => [
627                'NamespaceInfo',
628                'UserGroupManager',
629                'LanguageConverterFactory',
630                'GroupPermissionsLookup',
631            ]
632        ],
633        'Listgrants' => [
634            'class' => SpecialListGrants::class,
635            'services' => [
636                'GrantsLocalization',
637            ]
638        ],
639        'Listusers' => [
640            'class' => SpecialListUsers::class,
641            'services' => [
642                'LinkBatchFactory',
643                'ConnectionProvider',
644                'UserGroupManager',
645                'UserIdentityLookup',
646                'HideUserUtils',
647            ]
648        ],
649        'Listadmins' => [
650            'class' => SpecialListAdmins::class,
651        ],
652        'Listbots' => [
653            'class' => SpecialListBots::class,
654        ],
655        'Userrights' => [
656            'class' => SpecialUserRights::class,
657            'services' => [
658                'UserGroupManagerFactory',
659                'UserNameUtils',
660                'UserNamePrefixSearch',
661                'UserFactory',
662                'ActorStoreFactory',
663                'WatchlistManager',
664            ]
665        ],
666        'EditWatchlist' => [
667            'class' => SpecialEditWatchlist::class,
668            'services' => [
669                'WatchedItemStore',
670                'TitleParser',
671                'GenderCache',
672                'LinkBatchFactory',
673                'NamespaceInfo',
674                'WikiPageFactory',
675                'WatchlistManager',
676            ]
677        ],
678        'PasswordPolicies' => [
679            'class' => SpecialPasswordPolicies::class,
680            'services' => [
681                'UserGroupManager',
682            ]
683        ],
684
685        // Recent changes and logs
686        'Newimages' => [
687            'class' => SpecialNewFiles::class,
688            'services' => [
689                'MimeAnalyzer',
690                'GroupPermissionsLookup',
691                'ConnectionProvider',
692                'LinkBatchFactory',
693            ]
694        ],
695        'Log' => [
696            'class' => SpecialLog::class,
697            'services' => [
698                'LinkBatchFactory',
699                'ConnectionProvider',
700                'ActorNormalization',
701                'UserIdentityLookup',
702                'UserNameUtils',
703            ]
704        ],
705        'Watchlist' => [
706            'class' => SpecialWatchlist::class,
707            'services' => [
708                'WatchedItemStore',
709                'WatchlistManager',
710                'UserOptionsLookup',
711                'ChangeTagsStore',
712                'UserIdentityUtils',
713                'TempUserConfig',
714            ]
715        ],
716        'Newpages' => [
717            'class' => SpecialNewPages::class,
718            'services' => [
719                'LinkBatchFactory',
720                'ContentHandlerFactory',
721                'GroupPermissionsLookup',
722                'RevisionLookup',
723                'NamespaceInfo',
724                'UserOptionsLookup',
725                'RowCommentFormatter',
726                'ChangeTagsStore',
727            ]
728        ],
729        'Recentchanges' => [
730            'class' => SpecialRecentChanges::class,
731            'services' => [
732                'WatchedItemStore',
733                'MessageCache',
734                'UserOptionsLookup',
735                'ChangeTagsStore',
736                'UserIdentityUtils',
737                'TempUserConfig',
738            ]
739        ],
740        'Recentchangeslinked' => [
741            'class' => SpecialRecentChangesLinked::class,
742            'services' => [
743                'WatchedItemStore',
744                'MessageCache',
745                'UserOptionsLookup',
746                'SearchEngineFactory',
747                'ChangeTagsStore',
748                'UserIdentityUtils',
749                'TempUserConfig',
750            ]
751        ],
752        'Tags' => [
753            'class' => SpecialTags::class,
754            'services' => [
755                'ChangeTagsStore',
756            ]
757        ],
758
759        // Media reports and uploads
760        'Listfiles' => [
761            'class' => SpecialListFiles::class,
762            'services' => [
763                'RepoGroup',
764                'ConnectionProvider',
765                'CommentStore',
766                'UserNameUtils',
767                'UserNamePrefixSearch',
768                'CommentFormatter',
769                'LinkBatchFactory',
770            ]
771        ],
772        'Filepath' => [
773            'class' => SpecialFilepath::class,
774            'services' => [
775                'SearchEngineFactory',
776            ]
777        ],
778        'MediaStatistics' => [
779            'class' => SpecialMediaStatistics::class,
780            'services' => [
781                'MimeAnalyzer',
782                'ConnectionProvider',
783                'LinkBatchFactory',
784            ]
785        ],
786        'MIMEsearch' => [
787            'class' => SpecialMIMESearch::class,
788            'services' => [
789                'ConnectionProvider',
790                'LinkBatchFactory',
791                'LanguageConverterFactory',
792            ]
793        ],
794        'FileDuplicateSearch' => [
795            'class' => SpecialFileDuplicateSearch::class,
796            'services' => [
797                'LinkBatchFactory',
798                'RepoGroup',
799                'SearchEngineFactory',
800                'LanguageConverterFactory',
801            ]
802        ],
803        'Upload' => [
804            'class' => SpecialUpload::class,
805            'services' => [
806                'RepoGroup',
807                'UserOptionsLookup',
808                'NamespaceInfo',
809            ]
810        ],
811        'UploadStash' => [
812            'class' => SpecialUploadStash::class,
813            'services' => [
814                'RepoGroup',
815                'HttpRequestFactory',
816                'UrlUtils',
817                'ConnectionProvider',
818            ]
819        ],
820        'ListDuplicatedFiles' => [
821            'class' => SpecialListDuplicatedFiles::class,
822            'services' => [
823                'ConnectionProvider',
824                'LinkBatchFactory',
825            ]
826        ],
827
828        // Data and tools
829        'ApiSandbox' => [
830            'class' => SpecialApiSandbox::class,
831        ],
832        'RestSandbox' => [
833            'class' => SpecialRestSandbox::class,
834            'services' => [
835                'UrlUtils',
836            ]
837        ],
838        'Statistics' => [
839            'class' => SpecialStatistics::class,
840            'services' => [
841                'UserGroupManager',
842            ]
843        ],
844        'Allmessages' => [
845            'class' => SpecialAllMessages::class,
846            'services' => [
847                'LanguageFactory',
848                'LanguageNameUtils',
849                'LocalisationCache',
850                'ConnectionProvider',
851            ]
852        ],
853        'Version' => [
854            'class' => SpecialVersion::class,
855            'services' => [
856                'ParserFactory',
857                'UrlUtils',
858                'ConnectionProvider',
859            ]
860        ],
861        'Lockdb' => [
862            'class' => SpecialLockdb::class,
863        ],
864        'Unlockdb' => [
865            'class' => SpecialUnlockdb::class,
866        ],
867
868        // Redirecting special pages
869        'LinkSearch' => [
870            'class' => SpecialLinkSearch::class,
871            'services' => [
872                'ConnectionProvider',
873                'LinkBatchFactory',
874                'UrlUtils',
875            ]
876        ],
877        'Randompage' => [
878            'class' => SpecialRandomPage::class,
879            'services' => [
880                'ConnectionProvider',
881                'NamespaceInfo',
882            ]
883        ],
884        'RandomInCategory' => [
885            'class' => SpecialRandomInCategory::class,
886            'services' => [
887                'ConnectionProvider',
888            ]
889        ],
890        'Randomredirect' => [
891            'class' => SpecialRandomRedirect::class,
892            'services' => [
893                'ConnectionProvider',
894                'NamespaceInfo',
895            ]
896        ],
897        'Randomrootpage' => [
898            'class' => SpecialRandomRootPage::class,
899            'services' => [
900                'ConnectionProvider',
901                'NamespaceInfo',
902            ]
903        ],
904        'GoToInterwiki' => [
905            'class' => SpecialGoToInterwiki::class,
906        ],
907
908        // High use pages
909        'Mostlinkedcategories' => [
910            'class' => SpecialMostLinkedCategories::class,
911            'services' => [
912                'ConnectionProvider',
913                'LinkBatchFactory',
914                'LanguageConverterFactory',
915            ]
916        ],
917        'Mostimages' => [
918            'class' => SpecialMostImages::class,
919            'services' => [
920                'ConnectionProvider',
921                'LanguageConverterFactory',
922            ]
923        ],
924        'Mostinterwikis' => [
925            'class' => SpecialMostInterwikis::class,
926            'services' => [
927                'NamespaceInfo',
928                'ConnectionProvider',
929                'LinkBatchFactory',
930            ]
931        ],
932        'Mostlinked' => [
933            'class' => SpecialMostLinked::class,
934            'services' => [
935                'ConnectionProvider',
936                'LinkBatchFactory',
937                'LinksMigration',
938            ]
939        ],
940        'Mostlinkedtemplates' => [
941            'class' => SpecialMostLinkedTemplates::class,
942            'services' => [
943                'ConnectionProvider',
944                'LinkBatchFactory',
945                'LinksMigration',
946            ]
947        ],
948        'Mostcategories' => [
949            'class' => SpecialMostCategories::class,
950            'services' => [
951                'NamespaceInfo',
952                'ConnectionProvider',
953                'LinkBatchFactory',
954            ]
955        ],
956        'Mostrevisions' => [
957            'class' => SpecialMostRevisions::class,
958            'services' => [
959                // Same as for Fewestrevisions
960                'NamespaceInfo',
961                'ConnectionProvider',
962                'LinkBatchFactory',
963                'LanguageConverterFactory',
964            ]
965        ],
966
967        // Page tools
968        'ComparePages' => [
969            'class' => SpecialComparePages::class,
970            'services' => [
971                'RevisionLookup',
972                'ContentHandlerFactory',
973            ]
974        ],
975        'Export' => [
976            'class' => SpecialExport::class,
977            'services' => [
978                'ConnectionProvider',
979                'WikiExporterFactory',
980                'TitleFormatter',
981                'LinksMigration',
982            ]
983        ],
984        'Import' => [
985            'class' => SpecialImport::class,
986            'services' => [
987                'PermissionManager',
988                'WikiImporterFactory',
989            ]
990        ],
991        'Undelete' => [
992            'class' => SpecialUndelete::class,
993            'services' => [
994                'PermissionManager',
995                'RevisionStore',
996                'RevisionRenderer',
997                'ContentHandlerFactory',
998                'ChangeTagDefStore',
999                'LinkBatchFactory',
1000                'RepoGroup',
1001                'ConnectionProvider',
1002                'UserOptionsLookup',
1003                'WikiPageFactory',
1004                'SearchEngineFactory',
1005                'UndeletePageFactory',
1006                'ArchivedRevisionLookup',
1007                'CommentFormatter',
1008                'WatchlistManager',
1009            ],
1010        ],
1011        'Whatlinkshere' => [
1012            'class' => SpecialWhatLinksHere::class,
1013            'services' => [
1014                'ConnectionProvider',
1015                'LinkBatchFactory',
1016                'ContentHandlerFactory',
1017                'SearchEngineFactory',
1018                'NamespaceInfo',
1019                'TitleFactory',
1020                'LinksMigration',
1021            ]
1022        ],
1023        'MergeHistory' => [
1024            'class' => SpecialMergeHistory::class,
1025            'services' => [
1026                'MergeHistoryFactory',
1027                'LinkBatchFactory',
1028                'ConnectionProvider',
1029                'RevisionStore',
1030                'CommentFormatter',
1031            ]
1032        ],
1033        'ExpandTemplates' => [
1034            'class' => SpecialExpandTemplates::class,
1035            'services' => [
1036                'ParserFactory',
1037                'UserOptionsLookup',
1038                'Tidy',
1039            ],
1040        ],
1041        'ChangeContentModel' => [
1042            'class' => SpecialChangeContentModel::class,
1043            'services' => [
1044                'ContentHandlerFactory',
1045                'ContentModelChangeFactory',
1046                'SpamChecker',
1047                'RevisionLookup',
1048                'WikiPageFactory',
1049                'SearchEngineFactory',
1050                'CollationFactory',
1051            ],
1052        ],
1053
1054        // Other
1055        'Booksources' => [
1056            'class' => SpecialBookSources::class,
1057            'services' => [
1058                'RevisionLookup',
1059                'TitleFactory',
1060            ]
1061        ],
1062
1063        // Unlisted / redirects
1064        'ApiHelp' => [
1065            'class' => SpecialApiHelp::class,
1066            'services' => [
1067                'UrlUtils',
1068            ]
1069        ],
1070        'Blankpage' => [
1071            'class' => SpecialBlankpage::class,
1072        ],
1073        'DeletePage' => [
1074            'class' => SpecialDeletePage::class,
1075            'services' => [
1076                'SearchEngineFactory',
1077            ]
1078        ],
1079        'Diff' => [
1080            'class' => SpecialDiff::class,
1081        ],
1082        'EditPage' => [
1083            'class' => SpecialEditPage::class,
1084            'services' => [
1085                'SearchEngineFactory',
1086            ]
1087        ],
1088        'EditTags' => [
1089            'class' => SpecialEditTags::class,
1090            'services' => [
1091                'PermissionManager',
1092                'ChangeTagsStore',
1093            ],
1094        ],
1095        'Emailuser' => [
1096            'class' => SpecialEmailUser::class,
1097            'services' => [
1098                'UserNameUtils',
1099                'UserNamePrefixSearch',
1100                'UserOptionsLookup',
1101                'EmailUserFactory',
1102                'UserFactory',
1103            ]
1104        ],
1105        'Movepage' => [
1106            'class' => SpecialMovePage::class,
1107            'services' => [
1108                'MovePageFactory',
1109                'PermissionManager',
1110                'UserOptionsLookup',
1111                'ConnectionProvider',
1112                'ContentHandlerFactory',
1113                'NamespaceInfo',
1114                'LinkBatchFactory',
1115                'RepoGroup',
1116                'WikiPageFactory',
1117                'SearchEngineFactory',
1118                'WatchlistManager',
1119                'RestrictionStore',
1120                'TitleFactory',
1121                'DeletePageFactory',
1122            ]
1123        ],
1124        'Mycontributions' => [
1125            'class' => SpecialMycontributions::class,
1126        ],
1127        'MyLanguage' => [
1128            'class' => SpecialMyLanguage::class,
1129            'services' => [
1130                'LanguageNameUtils',
1131                'RedirectLookup'
1132            ]
1133        ],
1134        'Mylog' => [
1135            'class' => SpecialMylog::class,
1136        ],
1137        'Mypage' => [
1138            'class' => SpecialMypage::class,
1139        ],
1140        'Mytalk' => [
1141            'class' => SpecialMytalk::class,
1142        ],
1143        'PageHistory' => [
1144            'class' => SpecialPageHistory::class,
1145            'services' => [
1146                'SearchEngineFactory',
1147            ]
1148        ],
1149        'PageInfo' => [
1150            'class' => SpecialPageInfo::class,
1151            'services' => [
1152                'SearchEngineFactory',
1153            ]
1154        ],
1155        'ProtectPage' => [
1156            'class' => SpecialProtectPage::class,
1157            'services' => [
1158                'SearchEngineFactory',
1159            ]
1160        ],
1161        'Purge' => [
1162            'class' => SpecialPurge::class,
1163            'services' => [
1164                'SearchEngineFactory',
1165            ]
1166        ],
1167        'Myuploads' => [
1168            'class' => SpecialMyuploads::class,
1169        ],
1170        'AllMyUploads' => [
1171            'class' => SpecialAllMyUploads::class,
1172        ],
1173        'NewSection' => [
1174            'class' => SpecialNewSection::class,
1175            'services' => [
1176                'SearchEngineFactory',
1177            ]
1178        ],
1179        'PermanentLink' => [
1180            'class' => SpecialPermanentLink::class,
1181        ],
1182        'Redirect' => [
1183            'class' => SpecialRedirect::class,
1184            'services' => [
1185                'RepoGroup',
1186                'UserFactory',
1187            ]
1188        ],
1189        'Renameuser' => [
1190            'class' => SpecialRenameUser::class,
1191            'services' => [
1192                'ConnectionProvider',
1193                'ContentLanguage',
1194                'MovePageFactory',
1195                'PermissionManager',
1196                'TitleFactory',
1197                'UserFactory',
1198                'UserNamePrefixSearch',
1199                'UserNameUtils',
1200            ]
1201        ],
1202        'Revisiondelete' => [
1203            'class' => SpecialRevisionDelete::class,
1204            'services' => [
1205                'PermissionManager',
1206                'RepoGroup',
1207            ],
1208        ],
1209        'RunJobs' => [
1210            'class' => SpecialRunJobs::class,
1211            'services' => [
1212                'JobRunner',
1213                'ReadOnlyMode',
1214            ]
1215        ],
1216        'Specialpages' => [
1217            'class' => SpecialSpecialPages::class,
1218        ],
1219        'PageData' => [
1220            'class' => SpecialPageData::class,
1221        ],
1222        'Contribute' => [
1223            'class' => SpecialContribute::class,
1224        ],
1225        'TalkPage' => [
1226            'class' => SpecialTalkPage::class,
1227            'services' => [
1228                'MainConfig',
1229                'TitleParser',
1230            ],
1231        ],
1232    ];
1233
1234    /** @var array Special page name => class name */
1235    private $list;
1236
1237    /** @var array */
1238    private $aliases;
1239
1240    /** @var ServiceOptions */
1241    private $options;
1242
1243    /** @var Language */
1244    private $contLang;
1245
1246    /**
1247     * @var ObjectFactory
1248     * @noVarDump
1249     */
1250    private $objectFactory;
1251
1252    /**
1253     * @var HookContainer
1254     * @noVarDump
1255     */
1256    private $hookContainer;
1257
1258    /**
1259     * @var HookRunner
1260     * @noVarDump
1261     */
1262    private $hookRunner;
1263
1264    /**
1265     * @internal For use by ServiceWiring
1266     */
1267    public const CONSTRUCTOR_OPTIONS = [
1268        MainConfigNames::DisableInternalSearch,
1269        MainConfigNames::EmailAuthentication,
1270        MainConfigNames::EnableEmail,
1271        MainConfigNames::EnableJavaScriptTest,
1272        MainConfigNames::EnableSpecialMute,
1273        MainConfigNames::EnableEditRecovery,
1274        MainConfigNames::PageLanguageUseDB,
1275        MainConfigNames::SpecialPages,
1276    ];
1277
1278    /**
1279     * @var TitleFactory
1280     */
1281    private $titleFactory;
1282
1283    /**
1284     * @param ServiceOptions $options
1285     * @param Language $contLang
1286     * @param ObjectFactory $objectFactory
1287     * @param TitleFactory $titleFactory
1288     * @param HookContainer $hookContainer
1289     */
1290    public function __construct(
1291        ServiceOptions $options,
1292        Language $contLang,
1293        ObjectFactory $objectFactory,
1294        TitleFactory $titleFactory,
1295        HookContainer $hookContainer
1296    ) {
1297        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
1298        $this->options = $options;
1299        $this->contLang = $contLang;
1300        $this->objectFactory = $objectFactory;
1301        $this->titleFactory = $titleFactory;
1302        $this->hookContainer = $hookContainer;
1303        $this->hookRunner = new HookRunner( $hookContainer );
1304    }
1305
1306    /**
1307     * Returns a list of canonical special page names.
1308     * May be used to iterate over all registered special pages.
1309     *
1310     * @return string[]
1311     */
1312    public function getNames(): array {
1313        return array_keys( $this->getPageList() );
1314    }
1315
1316    /**
1317     * Get the special page list as an array
1318     *
1319     * @return array
1320     */
1321    private function getPageList(): array {
1322        if ( !is_array( $this->list ) ) {
1323            $this->list = self::CORE_LIST;
1324
1325            if ( !$this->options->get( MainConfigNames::DisableInternalSearch ) ) {
1326                $this->list['Search'] = [
1327                    'class' => SpecialSearch::class,
1328                    'services' => [
1329                        'SearchEngineConfig',
1330                        'SearchEngineFactory',
1331                        'NamespaceInfo',
1332                        'ContentHandlerFactory',
1333                        'InterwikiLookup',
1334                        'ReadOnlyMode',
1335                        'UserOptionsManager',
1336                        'LanguageConverterFactory',
1337                        'RepoGroup',
1338                        'SearchResultThumbnailProvider',
1339                        'TitleMatcher',
1340                    ]
1341                ];
1342            }
1343
1344            if ( $this->options->get( MainConfigNames::EmailAuthentication ) ) {
1345                $this->list['Confirmemail'] = [
1346                    'class' => SpecialConfirmEmail::class,
1347                    'services' => [
1348                        'UserFactory',
1349                    ]
1350                ];
1351                $this->list['Invalidateemail'] = [
1352                    'class' => SpecialEmailInvalidate::class,
1353                    'services' => [
1354                        'UserFactory',
1355                    ]
1356                ];
1357            }
1358
1359            if ( $this->options->get( MainConfigNames::EnableEmail ) ) {
1360                $this->list['ChangeEmail'] = [
1361                    'class' => SpecialChangeEmail::class,
1362                    'services' => [
1363                        'AuthManager',
1364                    ],
1365                ];
1366            }
1367
1368            if ( $this->options->get( MainConfigNames::EnableJavaScriptTest ) ) {
1369                $this->list['JavaScriptTest'] = [
1370                    'class' => SpecialJavaScriptTest::class
1371                ];
1372            }
1373
1374            if ( $this->options->get( MainConfigNames::EnableSpecialMute ) ) {
1375                $this->list['Mute'] = [
1376                    'class' => SpecialMute::class,
1377                    'services' => [
1378                        'CentralIdLookup',
1379                        'UserOptionsManager',
1380                        'UserIdentityLookup',
1381                        'UserIdentityUtils',
1382                    ]
1383                ];
1384            }
1385
1386            if ( $this->options->get( MainConfigNames::PageLanguageUseDB ) ) {
1387                $this->list['PageLanguage'] = [
1388                    'class' => SpecialPageLanguage::class,
1389                    'services' => [
1390                        'ContentHandlerFactory',
1391                        'LanguageNameUtils',
1392                        'ConnectionProvider',
1393                        'SearchEngineFactory',
1394                    ]
1395                ];
1396            }
1397
1398            if ( $this->options->get( MainConfigNames::EnableEditRecovery ) ) {
1399                $this->list['EditRecovery'] = [
1400                    'class' => SpecialEditRecovery::class,
1401                    'services' => [
1402                        'UserOptionsLookup',
1403                    ],
1404                ];
1405            }
1406
1407            // Add extension special pages
1408            $this->list = array_merge( $this->list,
1409                $this->options->get( MainConfigNames::SpecialPages ) );
1410
1411            // This hook can be used to disable unwanted core special pages
1412            // or conditionally register special pages.
1413            $this->hookRunner->onSpecialPage_initList( $this->list );
1414        }
1415
1416        return $this->list;
1417    }
1418
1419    /**
1420     * Initialise and return the list of special page aliases. Returns an array where
1421     * the key is an alias, and the value is the canonical name of the special page.
1422     * All registered special pages are guaranteed to map to themselves.
1423     * @return array
1424     */
1425    private function getAliasList(): array {
1426        if ( $this->aliases === null ) {
1427            $aliases = $this->contLang->getSpecialPageAliases();
1428            $pageList = $this->getPageList();
1429
1430            $this->aliases = [];
1431            $keepAlias = [];
1432
1433            // Force every canonical name to be an alias for itself.
1434            foreach ( $pageList as $name => $stuff ) {
1435                $caseFoldedAlias = $this->contLang->caseFold( $name );
1436                $this->aliases[$caseFoldedAlias] = $name;
1437                $keepAlias[$caseFoldedAlias] = 'canonical';
1438            }
1439
1440            // Check for $aliases being an array since Language::getSpecialPageAliases can return null
1441            if ( is_array( $aliases ) ) {
1442                foreach ( $aliases as $realName => $aliasList ) {
1443                    $first = true;
1444                    foreach ( $aliasList as $alias ) {
1445                        $caseFoldedAlias = $this->contLang->caseFold( $alias );
1446
1447                        if ( isset( $this->aliases[$caseFoldedAlias] ) &&
1448                            $realName === $this->aliases[$caseFoldedAlias]
1449                        ) {
1450                            $first = false;
1451                            // Ignore same-realName conflicts
1452                            continue;
1453                        }
1454
1455                        if ( !isset( $keepAlias[$caseFoldedAlias] ) ) {
1456                            $this->aliases[$caseFoldedAlias] = $realName;
1457                            if ( $first ) {
1458                                $keepAlias[$caseFoldedAlias] = 'first';
1459                            }
1460                        } elseif ( $first ) {
1461                            wfWarn( "First alias '$alias' for $realName conflicts with " .
1462                                "{$keepAlias[$caseFoldedAlias]} alias for " .
1463                                $this->aliases[$caseFoldedAlias]
1464                            );
1465                        }
1466                        $first = false;
1467                    }
1468                }
1469            }
1470        }
1471
1472        return $this->aliases;
1473    }
1474
1475    /**
1476     * Given a special page name with a possible subpage, return an array
1477     * where the first element is the special page name and the second is the
1478     * subpage.
1479     *
1480     * @param string $alias
1481     * @return array [ String, String|null ], or [ null, null ] if the page is invalid
1482     */
1483    public function resolveAlias( $alias ) {
1484        $bits = explode( '/', $alias, 2 );
1485
1486        $caseFoldedAlias = $this->contLang->caseFold( $bits[0] );
1487        $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1488        $aliases = $this->getAliasList();
1489        if ( !isset( $aliases[$caseFoldedAlias] ) ) {
1490            return [ null, null ];
1491        }
1492        $name = $aliases[$caseFoldedAlias];
1493        $par = $bits[1] ?? null; // T4087
1494
1495        return [ $name, $par ];
1496    }
1497
1498    /**
1499     * Check if a given name exist as a special page or as a special page alias
1500     *
1501     * @param string $name Name of a special page
1502     * @return bool True if a special page exists with this name
1503     */
1504    public function exists( $name ) {
1505        [ $title, /*...*/ ] = $this->resolveAlias( $name );
1506
1507        $specialPageList = $this->getPageList();
1508        return isset( $specialPageList[$title] );
1509    }
1510
1511    /**
1512     * Find the object with a given name and return it (or NULL)
1513     *
1514     * @param string $name Special page name, may be localised and/or an alias
1515     * @return SpecialPage|null SpecialPage object or null if the page doesn't exist
1516     */
1517    public function getPage( $name ) {
1518        [ $realName, /*...*/ ] = $this->resolveAlias( $name );
1519
1520        $specialPageList = $this->getPageList();
1521
1522        if ( isset( $specialPageList[$realName] ) ) {
1523            $rec = $specialPageList[$realName];
1524
1525            if ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
1526                $page = $this->objectFactory->createObject(
1527                    $rec,
1528                    [
1529                        'allowClassName' => true,
1530                        'allowCallable' => true
1531                    ]
1532                );
1533            } else {
1534                $page = null;
1535            }
1536
1537            if ( $page instanceof SpecialPage ) {
1538                $page->setHookContainer( $this->hookContainer );
1539                $page->setContentLanguage( $this->contLang );
1540                $page->setSpecialPageFactory( $this );
1541                return $page;
1542            }
1543
1544            // It's not a classname, nor a callback, nor a legacy constructor array,
1545