Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
23.50% covered (danger)
23.50%
43 / 183
CRAP
35.90% covered (danger)
35.90%
508 / 1415
Title
0.00% covered (danger)
0.00%
0 / 1
23.50% covered (danger)
23.50%
43 / 183
82559.34
35.90% covered (danger)
35.90%
508 / 1415
 getLanguageConverter
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getPageLanguageConverter
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getTitleFormatter
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getInterwikiLookup
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 newFromDBkey
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 newFromTitleValue
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 newFromLinkTarget
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
9 / 9
 castFromLinkTarget
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 newFromText
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 7
 newFromTextThrow
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 16
 newFromURL
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 8
 getTitleCache
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getSelectFields
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 newFromID
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 12
 newFromIDs
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 12
 newFromRow
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 loadFromRow
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 22
 makeTitle
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 9
 makeTitleSafe
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
8 / 8
 newMainPage
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (warning)
85.71%
6 / 7
 nameOf
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 10
 legalChars
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 convertByteClassToUnicodeClass
0.00% covered (danger)
0.00%
0 / 1
20.26
91.38% covered (success)
91.38%
53 / 58
 makeName
0.00% covered (danger)
0.00%
0 / 1
5.03
90.00% covered (success)
90.00%
9 / 10
 compare
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 isValid
0.00% covered (danger)
0.00%
0 / 1
8.01
94.12% covered (success)
94.12%
16 / 17
 isLocal
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 5
 isExternal
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getInterwiki
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 wasLocalInterwiki
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 isTrans
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getTransWikiID
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getTitleValue
0.00% covered (danger)
0.00%
0 / 1
3.24
70.00% covered (warning)
70.00%
7 / 10
 getText
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getPartialURL
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getDBkey
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getNamespace
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getContentModel
0.00% covered (danger)
0.00%
0 / 1
16.00
50.00% covered (danger)
50.00%
7 / 14
 hasContentModel
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 setContentModel
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 lazyFillContentModel
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 getNsText
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 10
 getSubjectNsText
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 getTalkNsText
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 canHaveTalkPage
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 canExist
0.00% covered (danger)
0.00%
0 / 1
6.22
81.82% covered (warning)
81.82%
9 / 11
 isWatchable
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 isSpecialPage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 isSpecial
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 6
 fixSpecialName
0.00% covered (danger)
0.00%
0 / 1
4.03
87.50% covered (warning)
87.50%
7 / 8
 inNamespace
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 inNamespaces
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
6 / 6
 hasSubjectNamespace
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 isContentPage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 isMovable
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
6 / 6
 isMainPage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 isSubpage
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 isConversionTable
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 isWikitextPage
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 isSiteConfigPage
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 isUserConfigPage
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 getSkinFromConfigSubpage
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 isUserCssConfigPage
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 isUserJsonConfigPage
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 isUserJsConfigPage
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 isSiteCssConfigPage
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 isSiteJsonConfigPage
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 isSiteJsConfigPage
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 isRawHtmlMessage
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 isTalkPage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getTalkPage
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
6 / 6
 getTalkPageIfDefined
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getSubjectPage
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
7 / 7
 warnIfPageCannotExist
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 11
 getOtherPage
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
 getDefaultNamespace
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getFragment
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 hasFragment
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getFragmentForURL
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
7 / 7
 setFragment
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 createFragmentTarget
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 prefix
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 10
 getPrefixedDBkey
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getPrefixedText
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 __toString
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getFullText
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 findSubpageDivider
0.00% covered (danger)
0.00%
0 / 1
156
0.00% covered (danger)
0.00%
0 / 18
 hasSubpagesEnabled
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getRootText
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (warning)
85.71%
6 / 7
 getRootTitle
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getBaseText
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (warning)
85.71%
6 / 7
 getBaseTitle
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getSubpageText
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (warning)
85.71%
6 / 7
 getSubpage
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getSubpageUrlForm
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 getPrefixedURL
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 fixUrlQueryArgs
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 13
 getFullURL
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 getFullUrlForRedirect
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 getLocalURL
0.00% covered (danger)
0.00%
0 / 1
139.80
35.42% covered (danger)
35.42%
17 / 48
 getLinkURL
0.00% covered (danger)
0.00%
0 / 1
5.12
83.33% covered (warning)
83.33%
5 / 6
 getInternalURL
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 getCanonicalURL
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 getEditURL
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 getFilteredRestrictionTypes
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 getRestrictionTypes
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 9
 getTitleProtection
0.00% covered (danger)
0.00%
0 / 1
6.99
42.86% covered (danger)
42.86%
3 / 7
 getTitleProtectionInternal
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 24
 deleteTitleProtection
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 isSemiProtected
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
10 / 10
 isProtected
0.00% covered (danger)
0.00%
0 / 1
8.38
81.82% covered (warning)
81.82%
9 / 11
 isNamespaceProtected
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
 isCascadeProtected
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 areCascadeProtectionSourcesLoaded
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 getCascadeProtectionSources
0.00% covered (danger)
0.00%
0 / 1
15.18
90.70% covered (success)
90.70%
39 / 43
 areRestrictionsLoaded
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getRestrictions
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
2 / 3
 getAllRestrictions
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
2 / 3
 getRestrictionExpiry
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
2 / 3
 areRestrictionsCascading
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 loadRestrictionsFromRows
0.00% covered (danger)
0.00%
0 / 1
17.98
72.73% covered (warning)
72.73%
24 / 33
 loadRestrictions
0.00% covered (danger)
0.00%
0 / 1
9
95.00% covered (success)
95.00%
38 / 40
 flushRestrictions
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 purgeExpiredRestrictions
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 24
 hasSubpages
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 9
 getSubpages
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 15
 isDeleted
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 11
 isDeletedQuick
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 11
 getArticleID
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 16
 isRedirect
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 9
 getLength
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 11
 getLatestRevID
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 11
 resetArticleID
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 17
 clearCaches
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 capitalize
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 secureAndSplit
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
16 / 16
 getLinksTo
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 20
 getTemplateLinksTo
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getLinksFrom
0.00% covered (danger)
0.00%
0 / 1
4.01
92.59% covered (success)
92.59%
25 / 27
 getTemplateLinksFrom
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getBrokenLinksFrom
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 14
 getCdnUrls
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 purgeSquid
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 isSingleRevRedirect
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 19
 getParentCategories
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 16
 getParentCategoryTree
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 10
 pageCond
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getRelativeRevisionID
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
8 / 8
 getPreviousRevisionID
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getNextRevisionID
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getFirstRevision
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 6
 getEarliestRevTime
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 isNewPage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 isBigDeletion
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 13
 estimateRevisionCount
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 7
 countRevisionsBetween
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 14
 getAuthorsBetween
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 18
 countAuthorsBetween
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 equals
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 isSubpageOf
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 3
 exists
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 isAlwaysKnown
0.00% covered (danger)
0.00%
0 / 1
8.79
76.92% covered (warning)
76.92%
10 / 13
 isKnown
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 hasSourceText
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 10
 getDefaultMessageText
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 8
 invalidateCache
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 21
 touchLinks
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 12
 getTouched
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 getNotificationTimestamp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 getNamespaceKey
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 11
 getRedirectsHere
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 18
 isValidRedirectTarget
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 8
 getBacklinkCache
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 canUseNoindex
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 getCategorySortkey
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 getDbPageLanguageCode
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 6
 getPageLanguage
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 15
 getPageViewLanguage
0.00% covered (danger)
0.00%
0 / 1
6.60
60.00% covered (warning)
60.00%
12 / 20
 getEditNotices
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 40
 loadFieldFromDB
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 10
 __sleep
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 __wakeup
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
<?php
/**
 * Representation of a title within MediaWiki.
 *
 * See Title.md
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 */
use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentity;
use Wikimedia\Assert\Assert;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
 * Represents a title within MediaWiki.
 * Optionally may contain an interwiki designation or namespace.
 * @note This class can fetch various kinds of data from the database;
 *       however, it does so inefficiently.
 * @note Consider using a TitleValue object instead. TitleValue is more lightweight
 *       and does not rely on global state or the database.
 */
class Title implements LinkTarget, IDBAccessObject {
    /** @var MapCacheLRU|null */
    private static $titleCache = null;
    /**
     * Title::newFromText maintains a cache to avoid expensive re-normalization of
     * commonly used titles. On a batch operation this can become a memory leak
     * if not bounded. After hitting this many titles reset the cache.
     */
    private const CACHE_MAX = 1000;
    /**
     * Used to be GAID_FOR_UPDATE define(). Used with getArticleID() and friends
     * to use the master DB and inject it into link cache.
     * @deprecated since 1.34, use Title::READ_LATEST instead.
     */
    public const GAID_FOR_UPDATE = 512;
    /**
     * Flag for use with factory methods like newFromLinkTarget() that have
     * a $forceClone parameter. If set, the method must return a new instance.
     * Without this flag, some factory methods may return existing instances.
     *
     * @since 1.33
     */
    public const NEW_CLONE = 'clone';
    /**
     * @name Private member variables
     * Please use the accessor functions instead.
     * @internal
     */
    // @{
    /** @var string Text form (spaces not underscores) of the main part */
    public $mTextform = '';
    /** @var string URL-encoded form of the main part */
    public $mUrlform = '';
    /** @var string Main part with underscores */
    public $mDbkeyform = '';
    /** @var int Namespace index, i.e. one of the NS_xxxx constants */
    public $mNamespace = NS_MAIN;
    /** @var string Interwiki prefix */
    public $mInterwiki = '';
    /** @var bool Was this Title created from a string with a local interwiki prefix? */
    private $mLocalInterwiki = false;
    /** @var string Title fragment (i.e. the bit after the #) */
    public $mFragment = '';
    /** @var int Article ID, fetched from the link cache on demand */
    public $mArticleID = -1;
    /** @var bool|int ID of most recent revision */
    protected $mLatestID = false;
    /**
     * @var bool|string ID of the page's content model, i.e. one of the
     *   CONTENT_MODEL_XXX constants
     */
    private $mContentModel = false;
    /**
     * @var bool If a content model was forced via setContentModel()
     *   this will be true to avoid having other code paths reset it
     */
    private $mForcedContentModel = false;
    /** @var int Estimated number of revisions; null of not loaded */
    private $mEstimateRevisions;
    /** @var array Array of groups allowed to edit this article */
    public $mRestrictions = [];
    /**
     * @var string|bool Comma-separated set of permission keys
     * indicating who can move or edit the page from the page table, (pre 1.10) rows.
     * Edit and move sections are separated by a colon
     * Example: "edit=autoconfirmed,sysop:move=sysop"
     */
    protected $mOldRestrictions = false;
    /** @var bool Cascade restrictions on this page to included templates and images? */
    public $mCascadeRestriction;
    /** Caching the results of getCascadeProtectionSources */
    public $mCascadingRestrictions;
    /** @var array When do the restrictions on this page expire? */
    protected $mRestrictionsExpiry = [];
    /** @var bool Are cascading restrictions in effect on this page? */
    protected $mHasCascadingRestrictions;
    /** @var array Where are the cascading restrictions coming from on this page? */
    public $mCascadeSources;
    /** @var bool Boolean for initialisation on demand */
    public $mRestrictionsLoaded = false;
    /**
     * Text form including namespace/interwiki, initialised on demand
     *
     * Only public to share cache with TitleFormatter
     *
     * @internal
     * @var string|null
     */
    public $prefixedText = null;
    /** @var mixed Cached value for getTitleProtection (create protection) */
    public $mTitleProtection;
    /**
     * @var int Namespace index when there is no namespace. Don't change the
     *   following default, NS_MAIN is hardcoded in several places. See T2696.
     *   Used primarily for {{transclusion}} tags.
     * @see newFromText()
     */
    public $mDefaultNamespace = NS_MAIN;
    /** @var int The page length, 0 for special pages */
    protected $mLength = -1;
    /** @var null|bool Is the article at this title a redirect? */
    public $mRedirect = null;
    /** @var bool Whether a page has any subpages */
    private $mHasSubpages;
    /** @var array|null The (string) language code of the page's language and content code. */
    private $mPageLanguage;
    /** @var string|bool|null The page language code from the database, null if not saved in
     * the database or false if not loaded, yet.
     */
    private $mDbPageLanguage = false;
    /** @var TitleValue|null A corresponding TitleValue object */
    private $mTitleValue = null;
    /** @var bool|null Would deleting this page be a big deletion? */
    private $mIsBigDeletion = null;
    /** @var bool|null Is the title known to be valid? */
    private $mIsValid = null;
    // @}
    /**
     * Shorthand for getting a Language Converter for specific language
     * @param Language $language Language of converter
     * @return ILanguageConverter
     */
    private function getLanguageConverter( $language ) : ILanguageConverter {
        return MediaWikiServices::getInstance()->getLanguageConverterFactory()
            ->getLanguageConverter( $language );
    }
    /**
     * Shorthand for getting a Language Converter for page's language
     * @return ILanguageConverter
     */
    private function getPageLanguageConverter() : ILanguageConverter {
        return $this->getLanguageConverter( $this->getPageLanguage() );
    }
    /**
     * B/C kludge: provide a TitleParser for use by Title.
     * Ideally, Title would have no methods that need this.
     * Avoid usage of this singleton by using TitleValue
     * and the associated services when possible.
     *
     * @return TitleFormatter
     */
    private static function getTitleFormatter() {
        return MediaWikiServices::getInstance()->getTitleFormatter();
    }
    /**
     * B/C kludge: provide an InterwikiLookup for use by Title.
     * Ideally, Title would have no methods that need this.
     * Avoid usage of this singleton by using TitleValue
     * and the associated services when possible.
     *
     * @return InterwikiLookup
     */
    private static function getInterwikiLookup() {
        return MediaWikiServices::getInstance()->getInterwikiLookup();
    }
    private function __construct() {
    }
    /**
     * Create a new Title from a prefixed DB key
     *
     * @param string $key The database key, which has underscores
     *     instead of spaces, possibly including namespace and
     *     interwiki prefixes
     * @return Title|null Title, or null on an error
     */
    public static function newFromDBkey( $key ) {
        $t = new self();
        try {
            $t->secureAndSplit( $key );
            return $t;
        } catch ( MalformedTitleException $ex ) {
            return null;
        }
    }
    /**
     * Returns a Title given a TitleValue.
     * If the given TitleValue is already a Title instance, that instance is returned,
     * unless $forceClone is "clone". If $forceClone is "clone" and the given TitleValue
     * is already a Title instance, that instance is copied using the clone operator.
     *
     * @deprecated since 1.34, use newFromLinkTarget or castFromLinkTarget
     *
     * @param TitleValue $titleValue Assumed to be safe.
     * @param string $forceClone set to NEW_CLONE to ensure a fresh instance is returned.
     *
     * @return Title
     */
    public static function newFromTitleValue( TitleValue $titleValue, $forceClone = '' ) {
        return self::newFromLinkTarget( $titleValue, $forceClone );
    }
    /**
     * Returns a Title given a LinkTarget.
     * If the given LinkTarget is already a Title instance, that instance is returned,
     * unless $forceClone is "clone". If $forceClone is "clone" and the given LinkTarget
     * is already a Title instance, that instance is copied using the clone operator.
     *
     * @param LinkTarget $linkTarget Assumed to be safe.
     * @param string $forceClone set to NEW_CLONE to ensure a fresh instance is returned.
     *
     * @return Title
     */
    public static function newFromLinkTarget( LinkTarget $linkTarget, $forceClone = '' ) {
        if ( $linkTarget instanceof Title ) {
            // Special case if it's already a Title object
            if ( $forceClone === self::NEW_CLONE ) {
                return clone $linkTarget;
            } else {
                return $linkTarget;
            }
        }
        return self::makeTitle(
            $linkTarget->getNamespace(),
            $linkTarget->getText(),
            $linkTarget->getFragment(),
            $linkTarget->getInterwiki()
        );
    }
    /**
     * Same as newFromLinkTarget, but if passed null, returns null.
     *
     * @param LinkTarget|null $linkTarget Assumed to be safe (if not null).
     *
     * @return Title|null
     */
    public static function castFromLinkTarget( $linkTarget ) {
        return $linkTarget ? self::newFromLinkTarget( $linkTarget ) : null;
    }
    /**
     * Create a new Title from text, such as what one would find in a link.
     * Decodes any HTML entities in the text.
     * Titles returned by this method are guaranteed to be valid.
     * Call canExist() to check if the Title represents an editable page.
     *
     * @note The Title instance returned by this method is not guaranteed to be a fresh instance.
     * It may instead be a cached instance created previously, with references to it remaining
     * elsewhere.
     *
     * @param string|int|null $text The link text; spaces, prefixes, and an
     *   initial ':' indicating the main namespace are accepted.
     * @param int $defaultNamespace The namespace to use if none is specified
     *   by a prefix.  If you want to force a specific namespace even if
     *   $text might begin with a namespace prefix, use makeTitle() or
     *   makeTitleSafe().
     * @throws InvalidArgumentException
     * @return Title|null Title or null if the Title could not be parsed because
     *         it is invalid.
     */
    public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
        // DWIM: Integers can be passed in here when page titles are used as array keys.
        if ( $text !== null && !is_string( $text ) && !is_int( $text ) ) {
            throw new InvalidArgumentException( '$text must be a string.' );
        }
        if ( $text === null ) {
            return null;
        }
        try {
            return self::newFromTextThrow( (string)$text, (int)$defaultNamespace );
        } catch ( MalformedTitleException $ex ) {
            return null;
        }
    }
    /**
     * Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,
     * rather than returning null.
     *
     * Titles returned by this method are guaranteed to be valid.
     * Call canExist() to check if the Title represents an editable page.
     *
     * @note The Title instance returned by this method is not guaranteed to be a fresh instance.
     * It may instead be a cached instance created previously, with references to it remaining
     * elsewhere.
     *
     * @see Title::newFromText
     *
     * @since 1.25
     * @param string $text Title text to check
     * @param int $defaultNamespace
     * @throws MalformedTitleException If the title is invalid.
     * @return Title
     */
    public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
        if ( is_object( $text ) ) {
            throw new MWException( '$text must be a string, given an object' );
        } elseif ( $text === null ) {
            // Legacy code relies on MalformedTitleException being thrown in this case
            // (happens when URL with no title in it is parsed). TODO fix
            throw new MalformedTitleException( 'title-invalid-empty' );
        }
        $titleCache = self::getTitleCache();
        // Wiki pages often contain multiple links to the same page.
        // Title normalization and parsing can become expensive on pages with many
        // links, so we can save a little time by caching them.
        // In theory these are value objects and won't get changed...
        if ( $defaultNamespace === NS_MAIN ) {
            $t = $titleCache->get( $text );
            if ( $t ) {
                return $t;
            }
        }
        // Convert things like &eacute; &#257; or &#x3017; into normalized (T16952) text
        $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
        $t = new Title();
        $dbKeyForm = strtr( $filteredText, ' ', '_' );
        $t->secureAndSplit( $dbKeyForm, (int)$defaultNamespace );
        if ( $defaultNamespace === NS_MAIN ) {
            $titleCache->set( $text, $t );
        }
        return $t;
    }
    /**
     * THIS IS NOT THE FUNCTION YOU WANT. Use Title::newFromText().
     *
     * Example of wrong and broken code:
     * $title = Title::newFromURL( $wgRequest->getVal( 'title' ) );
     *
     * Example of right code:
     * $title = Title::newFromText( $wgRequest->getVal( 'title' ) );
     *
     * Create a new Title from URL-encoded text. Ensures that
     * the given title's length does not exceed the maximum.
     *
     * @param string $url The title, as might be taken from a URL
     * @return Title|null The new object, or null on an error
     */
    public static function newFromURL( $url ) {
        $t = new Title();
        # For compatibility with old buggy URLs. "+" is usually not valid in titles,
        # but some URLs used it as a space replacement and they still come
        # from some external search tools.
        if ( strpos( self::legalChars(), '+' ) === false ) {
            $url = strtr( $url, '+', ' ' );
        }
        $dbKeyForm = strtr( $url, ' ', '_' );
        try {
            $t->secureAndSplit( $dbKeyForm );
            return $t;
        } catch ( MalformedTitleException $ex ) {
            return null;
        }
    }
    /**
     * @return MapCacheLRU
     */
    private static function getTitleCache() {
        if ( self::$titleCache === null ) {
            self::$titleCache = new MapCacheLRU( self::CACHE_MAX );
        }
        return self::$titleCache;
    }
    /**
     * Returns a list of fields that are to be selected for initializing Title
     * objects or LinkCache entries.
     *
     * @return array
     */
    protected static function getSelectFields() {
        global $wgPageLanguageUseDB;
        $fields = [
            'page_namespace', 'page_title', 'page_id',
            'page_len', 'page_is_redirect', 'page_latest',
            'page_content_model',
        ];
        if ( $wgPageLanguageUseDB ) {
            $fields[] = 'page_lang';
        }
        return $fields;
    }
    /**
     * Create a new Title from an article ID
     *
     * @param int $id The page_id corresponding to the Title to create
     * @param int $flags Bitfield of class READ_* constants
     * @return Title|null The new object, or null on an error
     */
    public static function newFromID( $id, $flags = 0 ) {
        $flags |= ( $flags & self::GAID_FOR_UPDATE ) ? self::READ_LATEST : 0; // b/c
        list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
        $row = wfGetDB( $index )->selectRow(
            'page',
            self::getSelectFields(),
            [ 'page_id' => $id ],
            __METHOD__,
            $options
        );
        if ( $row !== false ) {
            $title = self::newFromRow( $row );
        } else {
            $title = null;
        }
        return $title;
    }
    /**
     * Make an array of titles from an array of IDs
     *
     * @param int[] $ids Array of IDs
     * @return Title[] Array of Titles
     */
    public static function newFromIDs( $ids ) {
        if ( !count( $ids ) ) {
            return [];
        }
        $dbr = wfGetDB( DB_REPLICA );
        $res = $dbr->select(
            'page',
            self::getSelectFields(),
            [ 'page_id' => $ids ],
            __METHOD__
        );
        $titles = [];
        foreach ( $res as $row ) {
            $titles[] = self::newFromRow( $row );
        }
        return $titles;
    }
    /**
     * Make a Title object from a DB row
     *
     * @param stdClass $row Object database row (needs at least page_title,page_namespace)
     * @return Title Corresponding Title
     */
    public static function newFromRow( $row ) {
        $t = self::makeTitle( $row->page_namespace, $row->page_title );
        $t->loadFromRow( $row );
        return $t;
    }
    /**
     * Load Title object fields from a DB row.
     * If false is given, the title will be treated as non-existing.
     *
     * @param stdClass|bool $row Database row
     */
    public function loadFromRow( $row ) {
        if ( $row ) { // page found
            if ( isset( $row->page_id ) ) {
                $this->mArticleID = (int)$row->page_id;
            }
            if ( isset( $row->page_len ) ) {
                $this->mLength = (int)$row->page_len;
            }
            if ( isset( $row->page_is_redirect ) ) {
                $this->mRedirect = (bool)$row->page_is_redirect;
            }
            if ( isset( $row->page_latest ) ) {
                $this->mLatestID = (int)$row->page_latest;
            }
            if ( isset( $row->page_content_model ) ) {
                $this->lazyFillContentModel( $row->page_content_model );
            } else {
                $this->lazyFillContentModel( false ); // lazily-load getContentModel()
            }
            if ( isset( $row->page_lang ) ) {
                $this->mDbPageLanguage = (string)$row->page_lang;
            }
            if ( isset( $row->page_restrictions ) ) {
                $this->mOldRestrictions = $row->page_restrictions;
            }
        } else { // page not found
            $this->mArticleID = 0;
            $this->mLength = 0;
            $this->mRedirect = false;
            $this->mLatestID = 0;
            $this->lazyFillContentModel( false ); // lazily-load getContentModel()
        }
    }
    /**
     * Create a new Title from a namespace index and a DB key.
     *
     * It's assumed that $ns and $title are safe, for instance when
     * they came directly from the database or a special page name,
     * not from user input.
     *
     * No validation is applied. For convenience, spaces are normalized
     * to underscores, so that e.g. user_text fields can be used directly.
     *
     * @note This method may return Title objects that are "invalid"
     * according to the isValid() method. This is usually caused by
     * configuration changes: e.g. a namespace that was once defined is
     * no longer configured, or a character that was once allowed in
     * titles is now forbidden.
     *
     * @param int $ns The namespace of the article
     * @param string $title The unprefixed database key form
     * @param string $fragment The link fragment (after the "#")
     * @param string $interwiki The interwiki prefix
     * @return Title The new object
     */
    public static function makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
        $t = new Title();
        $t->mInterwiki = $interwiki;
        $t->mFragment = $fragment;
        $t->mNamespace = $ns = (int)$ns;
        $t->mDbkeyform = strtr( $title, ' ', '_' );
        $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
        $t->mUrlform = wfUrlencode( $t->mDbkeyform );
        $t->mTextform = strtr( $title, '_', ' ' );
        return $t;
    }
    /**
     * Create a new Title from a namespace index and a DB key.
     * The parameters will be checked for validity, which is a bit slower
     * than makeTitle() but safer for user-provided data.
     *
     * The Title object returned by this method is guaranteed to be valid.
     * Call canExist() to check if the Title represents an editable page.
     *
     * @param int $ns The namespace of the article
     * @param string $title Database key form
     * @param string $fragment The link fragment (after the "#")
     * @param string $interwiki Interwiki prefix
     * @return Title|null The new object, or null on an error
     */
    public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
        // NOTE: ideally, this would just call makeTitle() and then isValid(),
        // but presently, that means more overhead on a potential performance hotspot.
        if ( !MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $ns ) ) {
            return null;
        }
        $t = new Title();
        $dbKeyForm = self::makeName( $ns, $title, $fragment, $interwiki, true );
        try {
            $t->secureAndSplit( $dbKeyForm );
            return $t;
        } catch ( MalformedTitleException $ex ) {
            return null;
        }
    }
    /**
     * Create a new Title for the Main Page
     *
     * This uses the 'mainpage' interface message, which could be specified in
     * `$wgForceUIMsgAsContentMsg`. If that is the case, then calling this method
     * will use the user language, which would involve initialising the session
     * via `RequestContext::getMain()->getLanguage()`. For session-less endpoints,
     * be sure to pass in a MessageLocalizer (such as your own RequestContext,
     * or ResourceloaderContext) to prevent an error.
     *
     * @note The Title instance returned by this method is not guaranteed to be a fresh instance.
     * It may instead be a cached instance created previously, with references to it remaining
     * elsewhere.
     *
     * @param MessageLocalizer|null $localizer An optional context to use (since 1.34)
     * @return Title
     */
    public static function newMainPage( MessageLocalizer $localizer = null ) {
        if ( $localizer ) {
            $msg = $localizer->msg( 'mainpage' );
        } else {
            $msg = wfMessage( 'mainpage' );
        }
        $title = self::newFromText( $msg->inContentLanguage()->text() );
        // Every page renders at least one link to the Main Page (e.g. sidebar).
        // If the localised value is invalid, don't produce fatal errors that
        // would make the wiki inaccessible (and hard to fix the invalid message).
        // Gracefully fallback...
        if ( !$title ) {
            $title = self::newFromText( 'Main Page' );
        }
        return $title;
    }
    /**
     * Get the prefixed DB key associated with an ID
     *
     * @param int $id The page_id of the article
     * @return string|null An object representing the article, or null if no such article was found
     * @deprecated since 1.36, use Title::newFromID( $id )->getPrefixedDBkey() instead.
     */
    public static function nameOf( $id ) {
        wfDeprecated( __METHOD__, '1.36' );
        $dbr = wfGetDB( DB_REPLICA );
        $s = $dbr->selectRow(
            'page',
            [ 'page_namespace', 'page_title' ],
            [ 'page_id' => $id ],
            __METHOD__
        );
        if ( $s === false ) {
            return null;
        }
        return self::makeName( $s->page_namespace, $s->page_title );
    }
    /**
     * Get a regex character class describing the legal characters in a link
     *
     * @return string The list of characters, not delimited
     */
    public static function legalChars() {
        global $wgLegalTitleChars;
        return $wgLegalTitleChars;
    }
    /**
     * Utility method for converting a character sequence from bytes to Unicode.
     *
     * Primary usecase being converting $wgLegalTitleChars to a sequence usable in
     * javascript, as PHP uses UTF-8 bytes where javascript uses Unicode code units.
     *
     * @param string $byteClass
     * @return string
     */
    public static function convertByteClassToUnicodeClass( $byteClass ) {
        $length = strlen( $byteClass );
        // Input token queue
        $x0 = $x1 = $x2 = '';
        // Decoded queue
        $d0 = $d1 = $d2 = '';
        // Decoded integer codepoints
        $ord0 = $ord1 = $ord2 = 0;
        // Re-encoded queue
        $r0 = $r1 = $r2 = '';
        // Output
        $out = '';
        // Flags
        $allowUnicode = false;
        for ( $pos = 0; $pos < $length; $pos++ ) {
            // Shift the queues down
            $x2 = $x1;
            $x1 = $x0;
            $d2 = $d1;
            $d1 = $d0;
            $ord2 = $ord1;
            $ord1 = $ord0;
            $r2 = $r1;
            $r1 = $r0;
            // Load the current input token and decoded values
            $inChar = $byteClass[$pos];
            if ( $inChar == '\\' ) {
                if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
                    $x0 = $inChar . $m[0];
                    $d0 = chr( hexdec( $m[1] ) );
                    $pos += strlen( $m[0] );
                } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
                    $x0 = $inChar . $m[0];
                    $d0 = chr( octdec( $m[0] ) );
                    $pos += strlen( $m[0] );
                } elseif ( $pos + 1 >= $length ) {
                    $x0 = $d0 = '\\';
                } else {
                    $d0 = $byteClass[$pos + 1];
                    $x0 = $inChar . $d0;
                    $pos += 1;
                }
            } else {
                $x0 = $d0 = $inChar;
            }
            $ord0 = ord( $d0 );
            // Load the current re-encoded value
            if ( $ord0 < 32 || $ord0 == 0x7f ) {
                $r0 = sprintf( '\x%02x', $ord0 );
            } elseif ( $ord0 >= 0x80 ) {
                // Allow unicode if a single high-bit character appears
                $r0 = sprintf( '\x%02x', $ord0 );
                $allowUnicode = true;
                // @phan-suppress-next-line PhanParamSuspiciousOrder false positive
            } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
                $r0 = '\\' . $d0;
            } else {
                $r0 = $d0;
            }
            // Do the output
            if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
                // Range
                if ( $ord2 > $ord0 ) {
                    // Empty range
                } elseif ( $ord0 >= 0x80 ) {
                    // Unicode range
                    $allowUnicode = true;
                    if ( $ord2 < 0x80 ) {
                        // Keep the non-unicode section of the range
                        $out .= "$r2-\\x7F";
                    }
                } else {
                    // Normal range
                    $out .= "$r2-$r0";
                }
                // Reset state to the initial value
                $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
            } elseif ( $ord2 < 0x80 ) {
                // ASCII character
                $out .= $r2;
            }
        }
        // @phan-suppress-next-line PhanSuspiciousValueComparison
        if ( $ord1 < 0x80 ) {
            $out .= $r1;
        }
        if ( $ord0 < 0x80 ) {
            $out .= $r0;
        }
        if ( $allowUnicode ) {
            $out .= '\u0080-\uFFFF';
        }
        return $out;
    }
    /**
     * Make a prefixed DB key from a DB key and a namespace index
     *
     * @param int $ns Numerical representation of the namespace
     * @param string $title The DB key form the title
     * @param string $fragment The link fragment (after the "#")
     * @param string $interwiki The interwiki prefix
     * @param bool $canonicalNamespace If true, use the canonical name for
     *   $ns instead of the localized version.
     * @return string The prefixed form of the title
     */
    public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
        $canonicalNamespace = false
    ) {
        if ( $canonicalNamespace ) {
            $namespace = MediaWikiServices::getInstance()->getNamespaceInfo()->
                getCanonicalName( $ns );
        } else {
            $namespace = MediaWikiServices::getInstance()->getContentLanguage()->getNsText( $ns );
        }
        $name = $namespace == '' ? $title : "$namespace:$title";
        if ( strval( $interwiki ) != '' ) {
            $name = "$interwiki:$name";
        }
        if ( strval( $fragment ) != '' ) {
            $name .= '#' . $fragment;
        }
        return $name;
    }
    /**
     * Callback for usort() to do title sorts by (namespace, title)
     *
     * @param LinkTarget $a
     * @param LinkTarget $b
     *
     * @return int Result of string comparison, or namespace comparison
     */
    public static function compare( LinkTarget $a, LinkTarget $b ) {
        return $a->getNamespace() <=> $b->getNamespace()
            ?: strcmp( $a->getText(), $b->getText() );
    }
    /**
     * Returns true if the title is a valid link target, and that it has been
     * properly normalized. This method checks that the title is syntactically valid,
     * and that the namespace it refers to exists.
     *
     * Titles constructed using newFromText() or makeTitleSafe() are always valid.
     *
     * @note Code that wants to check whether the title can represent a page that can
     * be created and edited should use canExist() instead. Examples of valid titles
     * that cannot "exist" are Special pages, interwiki links, and on-page section links
     * that only have the fragment part set.
     *
     * @see canExist()
     *
     * @return bool
     */
    public function isValid() {
        if ( $this->mIsValid !== null ) {
            return $this->mIsValid;
        }
        try {
            $text = $this->getFullText();
            $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
            '@phan-var MediaWikiTitleCodec $titleCodec';
            $parts = $titleCodec->splitTitleString( $text, $this->mNamespace );
            // Check that nothing changed!
            // This ensures that $text was already perperly normalized.
            if ( $parts['fragment'] !== $this->mFragment
                || $parts['interwiki'] !== $this->mInterwiki
                || $parts['local_interwiki'] !== $this->mLocalInterwiki
                || $parts['namespace'] !== $this->mNamespace
                || $parts['dbkey'] !== $this->mDbkeyform
            ) {
                $this->mIsValid = false;
                return $this->mIsValid;
            }
        } catch ( MalformedTitleException $ex ) {
            $this->mIsValid = false;
            return $this->mIsValid;
        }
        $this->mIsValid = true;
        return $this->mIsValid;
    }
    /**
     * Determine whether the object refers to a page within
     * this project (either this wiki or a wiki with a local
     * interwiki, see https://www.mediawiki.org/wiki/Manual:Interwiki_table#iw_local )
     *
     * @return bool True if this is an in-project interwiki link or a wikilink, false otherwise
     */
    public function isLocal() {
        if ( $this->isExternal() ) {
            $iw = self::getInterwikiLookup()->fetch( $this->mInterwiki );
            if ( $iw ) {
                return $iw->isLocal();
            }
        }
        return true;
    }
    /**
     * Is this Title interwiki?
     *
     * @return bool
     */
    public function isExternal() {
        return $this->mInterwiki !== '';
    }
    /**
     * Get the interwiki prefix
     *
     * Use Title::isExternal to check if a interwiki is set
     *
     * @return string Interwiki prefix
     */
    public function getInterwiki() {
        return $this->mInterwiki;
    }
    /**
     * Was this a local interwiki link?
     *
     * @return bool
     */
    public function wasLocalInterwiki() {
        return $this->mLocalInterwiki;
    }
    /**
     * Determine whether the object refers to a page within
     * this project and is transcludable.
     *
     * @return bool True if this is transcludable
     */
    public function isTrans() {
        if ( !$this->isExternal() ) {
            return false;
        }
        return self::getInterwikiLookup()->fetch( $this->mInterwiki )->isTranscludable();
    }
    /**
     * Returns the DB name of the distant wiki which owns the object.
     *
     * @return string|false The DB name
     */
    public function getTransWikiID() {
        if ( !$this->isExternal() ) {
            return false;
        }
        return self::getInterwikiLookup()->fetch( $this->mInterwiki )->getWikiID();
    }
    /**
     * Get a TitleValue object representing this Title.
     *
     * @note Not all valid Titles have a corresponding valid TitleValue
     * (e.g. TitleValues cannot represent page-local links that have a
     * fragment but no title text).
     *
     * @return TitleValue|null
     */
    public function getTitleValue() {
        if ( $this->mTitleValue === null ) {
            try {
                $this->mTitleValue = new TitleValue(
                    $this->mNamespace,
                    $this->mDbkeyform,
                    $this->mFragment,
                    $this->mInterwiki
                );
            } catch ( InvalidArgumentException $ex ) {
                wfDebug( __METHOD__ . ': Can\'t create a TitleValue for [[' .
                    $this->getPrefixedText() . ']]: ' . $ex->getMessage() );
            }
        }
        return $this->mTitleValue;
    }
    /**
     * Get the text form (spaces not underscores) of the main part
     *
     * @return string Main part of the title
     */
    public function getText() {
        return $this->mTextform;
    }
    /**
     * Get the URL-encoded form of the main part
     *
     * @return string Main part of the title, URL-encoded
     */
    public function getPartialURL() {
        return $this->mUrlform;
    }