Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.51% covered (warning)
86.51%
2231 / 2579
58.00% covered (warning)
58.00%
87 / 150
CRAP
0.00% covered (danger)
0.00%
0 / 1
Parser
86.54% covered (warning)
86.54%
2231 / 2578
58.00% covered (warning)
58.00%
87 / 150
2560.67
0.00% covered (danger)
0.00%
0 / 1
 __construct
96.30% covered (success)
96.30%
26 / 27
0.00% covered (danger)
0.00%
0 / 1
2
 __destruct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 __clone
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 firstCallInit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 clearState
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
1 / 1
1
 resetOutput
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getParseTime
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 parse
92.86% covered (success)
92.86%
52 / 56
0.00% covered (danger)
0.00%
0 / 1
9.03
 makeLimitReport
98.28% covered (success)
98.28%
57 / 58
0.00% covered (danger)
0.00%
0 / 1
9
 recursiveTagParse
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 recursiveTagParseFully
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 parseExtensionTagAsTopLevelDoc
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 preprocess
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
 recursivePreprocess
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPreloadText
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 setUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPage
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getPage
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 getOutputType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setOutputType
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 nextLinkID
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setLinkID
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTargetLanguage
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getUserIdentity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPreprocessor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLinkRenderer
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getMagicWordFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBadFileLookup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 extractTagsAndParams
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
56
 getStripList
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStripState
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 insertStripItem
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 handleTables
99.14% covered (success)
99.14%
115 / 116
0.00% covered (danger)
0.00%
0 / 1
33
 internalParse
93.55% covered (success)
93.55%
29 / 31
0.00% covered (danger)
0.00%
0 / 1
4.00
 getTargetLanguageConverter
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getContentLanguageConverter
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getHookContainer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaceInfo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMiserMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHookRunner
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 internalParseHalfParsed
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
7
 handleMagicLinks
91.30% covered (success)
91.30%
21 / 23
0.00% covered (danger)
0.00%
0 / 1
2.00
 magicLinkCallback
96.30% covered (success)
96.30%
52 / 54
0.00% covered (danger)
0.00%
0 / 1
16
 makeFreeExternalLink
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
9
 handleHeadings
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 handleAllQuotes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 doQuotes
98.28% covered (success)
98.28%
114 / 116
0.00% covered (danger)
0.00%
0 / 1
46
 handleExternalLinks
97.22% covered (success)
97.22%
35 / 36
0.00% covered (danger)
0.00%
0 / 1
7
 getExternalLinkRel
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getExternalLinkAttribs
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 normalizeLinkUrl
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
11
 normalizeUrlComponent
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 maybeMakeExternalImage
44.83% covered (danger)
44.83%
13 / 29
0.00% covered (danger)
0.00%
0 / 1
65.54
 handleInternalLinks
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 handleInternalLinks2
96.73% covered (success)
96.73%
148 / 153
0.00% covered (danger)
0.00%
0 / 1
48
 makeKnownLinkHolder
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 armorLinks
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 expandMagicVariable
47.37% covered (danger)
47.37%
9 / 19
0.00% covered (danger)
0.00%
0 / 1
6.33
 initializeVariables
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 preprocessToDom
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 replaceVariables
55.56% covered (warning)
55.56%
10 / 18
0.00% covered (danger)
0.00%
0 / 1
11.30
 setUseParsoidFragments
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 useParsoidFragments
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 limitationWarn
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 braceSubstitution
85.65% covered (warning)
85.65%
185 / 216
0.00% covered (danger)
0.00%
0 / 1
107.86
 callParserFunction
95.24% covered (success)
95.24%
40 / 42
0.00% covered (danger)
0.00%
0 / 1
16
 getTemplateDom
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
8
 fetchCurrentRevisionRecordOfTitle
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 isCurrentRevisionOfTitleCached
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 defaultFetchRevisionRecord
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 statelessFetchRevisionRecord
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 fetchTemplateAndTitle
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
6.06
 defaultFetchTemplate
94.94% covered (success)
94.94%
75 / 79
0.00% covered (danger)
0.00%
0 / 1
24.07
 statelessFetchTemplate
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 fetchFileAndTitle
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 fetchFileNoRegister
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 interwikiTransclude
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
110
 argSubstitution
86.36% covered (warning)
86.36%
19 / 22
0.00% covered (danger)
0.00%
0 / 1
12.37
 tagNeedsNowikiStrippedInTagPF
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 extensionSubstitution
72.55% covered (warning)
72.55%
37 / 51
0.00% covered (danger)
0.00%
0 / 1
37.93
 incrementIncludeSize
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 incrementExpensiveFunctionCount
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 handleDoubleUnderscore
97.06% covered (success)
97.06%
33 / 34
0.00% covered (danger)
0.00%
0 / 1
17
 addTrackingCategory
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 msg
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 cleanUpTocLine
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
16
 finalizeHeadings
99.31% covered (success)
99.31%
143 / 144
0.00% covered (danger)
0.00%
0 / 1
43
 localizeTOC
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
 preSaveTransform
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 pstPass2
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
5
 getUserSig
76.92% covered (warning)
76.92%
30 / 39
0.00% covered (danger)
0.00%
0 / 1
17.77
 validateSig
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 cleanSig
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
4
 cleanSigInSig
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 replaceTableOfContentsMarker
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
4.00
 startExternalParse
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 startParse
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 transformMsg
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 setHook
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 clearTagHooks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setFunctionHook
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 getFunctionHooks
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 replaceLinkHoldersPrivate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 replaceLinkHoldersText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderImageGallery
95.00% covered (success)
95.00%
95 / 100
0.00% covered (danger)
0.00%
0 / 1
25
 getImageParams
80.77% covered (warning)
80.77%
21 / 26
0.00% covered (danger)
0.00%
0 / 1
8.46
 makeImageHtml
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 makeImage
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 makeImageInternal
98.98% covered (success)
98.98%
97 / 98
0.00% covered (danger)
0.00%
0 / 1
36
 parseLinkParameter
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
6
 modifyImageHtml
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 stripAltText
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getTags
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFunctionSynonyms
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUrlProtocols
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 extractSections
94.12% covered (success)
94.12%
48 / 51
0.00% covered (danger)
0.00%
0 / 1
21.09
 getSection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 replaceSection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFlatSectionInfo
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
12
 getRevisionId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRevisionRecordObject
72.22% covered (warning)
72.22%
13 / 18
0.00% covered (danger)
0.00%
0 / 1
9.37
 getRevisionTimestamp
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 getRevisionUser
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
6
 getRevisionSize
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getSectionNameFromStrippedText
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 makeAnchor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeLegacyAnchor
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 guessSectionNameFromWikiText
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 guessLegacySectionNameFromWikiText
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 guessSectionNameFromStrippedText
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 normalizeSectionName
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
2.26
 stripSectionName
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 markerSkipCallback
87.50% covered (warning)
87.50%
14 / 16
0.00% covered (danger)
0.00%
0 / 1
4.03
 killMarkers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parseWidthParam
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
9.01
 lock
60.00% covered (warning)
60.00%
12 / 20
0.00% covered (danger)
0.00%
0 / 1
6.60
 isLocked
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 stripOuterParagraph
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 splitDisplayTitle
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 splitPageTitle
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
4
 formatPageTitle
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 unformatPageTitle
90.48% covered (success)
90.48%
19 / 21
0.00% covered (danger)
0.00%
0 / 1
4.01
 extractBody
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 setOutputFlag
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * PHP parser that converts wiki markup to HTML.
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Parser
8 */
9
10namespace MediaWiki\Parser;
11
12use BadMethodCallException;
13use DateTime;
14use DateTimeZone;
15use Exception;
16use InvalidArgumentException;
17use LogicException;
18use MediaWiki\Category\TrackingCategories;
19use MediaWiki\Config\ServiceOptions;
20use MediaWiki\Content\TextContent;
21use MediaWiki\Context\RequestContext;
22use MediaWiki\Debug\DeprecationHelper;
23use MediaWiki\FileRepo\File\File;
24use MediaWiki\FileRepo\RepoGroup;
25use MediaWiki\Gallery\Exception\ImageGalleryClassNotFoundException;
26use MediaWiki\Gallery\ImageGalleryBase;
27use MediaWiki\HookContainer\HookContainer;
28use MediaWiki\HookContainer\HookRunner;
29use MediaWiki\Html\Html;
30use MediaWiki\Html\HtmlHelper;
31use MediaWiki\Http\HttpRequestFactory;
32use MediaWiki\Language\ILanguageConverter;
33use MediaWiki\Language\Language;
34use MediaWiki\Language\LanguageCode;
35use MediaWiki\Language\LanguageConverterFactory;
36use MediaWiki\Language\LanguageFactory;
37use MediaWiki\Language\LanguageNameUtils;
38use MediaWiki\Language\MessageLocalizer;
39use MediaWiki\Language\RawMessage;
40use MediaWiki\Linker\Linker;
41use MediaWiki\Linker\LinkRenderer;
42use MediaWiki\Linker\LinkRendererFactory;
43use MediaWiki\MainConfigNames;
44use MediaWiki\Media\MediaHandler;
45use MediaWiki\MediaWikiServices;
46use MediaWiki\Message\Message;
47use MediaWiki\Page\CacheKeyHelper;
48use MediaWiki\Page\File\BadFileLookup;
49use MediaWiki\Page\LinkAlwaysKnownLookup;
50use MediaWiki\Page\LinkCache;
51use MediaWiki\Page\PageIdentity;
52use MediaWiki\Page\PageReference;
53use MediaWiki\Preferences\SignatureValidatorFactory;
54use MediaWiki\Profiler\SectionProfiler;
55use MediaWiki\Request\FauxRequest;
56use MediaWiki\Revision\RevisionAccessException;
57use MediaWiki\Revision\RevisionLookup;
58use MediaWiki\Revision\RevisionRecord;
59use MediaWiki\Revision\SlotRecord;
60use MediaWiki\ShadowPage\ShadowPageLoader;
61use MediaWiki\SpecialPage\SpecialPage;
62use MediaWiki\SpecialPage\SpecialPageFactory;
63use MediaWiki\Tidy\TidyDriverBase;
64use MediaWiki\Title\MalformedTitleException;
65use MediaWiki\Title\NamespaceInfo;
66use MediaWiki\Title\Title;
67use MediaWiki\Title\TitleFormatter;
68use MediaWiki\Title\TitleValue;
69use MediaWiki\User\Options\UserOptionsLookup;
70use MediaWiki\User\User;
71use MediaWiki\User\UserFactory;
72use MediaWiki\User\UserIdentity;
73use MediaWiki\User\UserNameUtils;
74use MediaWiki\Utils\MWTimestamp;
75use MediaWiki\Utils\UrlUtils;
76use MediaWiki\Xml\Xml;
77use Psr\Log\LoggerInterface;
78use RuntimeException;
79use UnexpectedValueException;
80use Wikimedia\Bcp47Code\Bcp47CodeValue;
81use Wikimedia\HtmlArmor\HtmlArmor;
82use Wikimedia\IPUtils;
83use Wikimedia\ObjectCache\MapCacheLRU;
84use Wikimedia\ObjectCache\WANObjectCache;
85use Wikimedia\Parsoid\Core\LinkTarget;
86use Wikimedia\Parsoid\Core\SectionMetadata;
87use Wikimedia\Parsoid\Core\TOCData;
88use Wikimedia\Parsoid\DOM\Comment;
89use Wikimedia\Parsoid\DOM\DocumentFragment;
90use Wikimedia\Parsoid\DOM\Element;
91use Wikimedia\Parsoid\DOM\Node;
92use Wikimedia\Parsoid\Utils\DOMCompat;
93use Wikimedia\Parsoid\Utils\DOMUtils;
94use Wikimedia\RemexHtml\Serializer\SerializerNode;
95use Wikimedia\ScopedCallback;
96use Wikimedia\StringUtils\StringUtils;
97
98/**
99 * @defgroup Parser Parser
100 */
101
102/**
103 * PHP Parser - Processes wiki markup (which uses a more user-friendly
104 * syntax, such as "[[link]]" for making links), and provides a one-way
105 * transformation of that wiki markup it into (X)HTML output / markup
106 * (which in turn the browser understands, and can display).
107 *
108 * There are seven main entry points into the Parser class:
109 *
110 * - Parser::parse()
111 *     produces HTML output
112 * - Parser::preSaveTransform()
113 *     produces altered wiki markup
114 * - Parser::preprocess()
115 *     removes HTML comments and expands templates
116 * - Parser::cleanSig() and Parser::cleanSigInSig()
117 *     cleans a signature before saving it to preferences
118 * - Parser::getSection()
119 *     return the content of a section from an article for section editing
120 * - Parser::replaceSection()
121 *     replaces a section by number inside an article
122 * - Parser::getPreloadText()
123 *     removes <noinclude> sections and <includeonly> tags
124 *
125 * @warning $wgTitle or $wgRequest or $wgLang. Keep them away!
126 *
127 * @par Settings:
128 * $wgNamespacesWithSubpages
129 *
130 * @par Settings only within ParserOptions:
131 * $wgAllowExternalImages
132 * $wgAllowSpecialInclusion
133 * $wgInterwikiMagic
134 * $wgMaxArticleSize
135 *
136 * @ingroup Parser
137 */
138#[\AllowDynamicProperties]
139class Parser implements MessageLocalizer {
140    use DeprecationHelper;
141
142    # Flags for Parser::setFunctionHook
143    public const SFH_NO_HASH = 1;
144    public const SFH_OBJECT_ARGS = 2;
145
146    # Constants needed for external link processing
147    /**
148     * Everything except bracket, space, or control characters.
149     * \p{Zs} is unicode 'separator, space' category. It covers the space 0x20
150     * as well as U+3000 is IDEOGRAPHIC SPACE for T21052.
151     * \x{FFFD} is the Unicode replacement character, which the HTML5 spec
152     * uses to replace invalid HTML characters.
153     */
154    public const EXT_LINK_URL_CLASS = '[^][<>"\\x00-\\x20\\x7F\p{Zs}\x{FFFD}]';
155    /**
156     * Simplified expression to match an IPv4 or IPv6 address, or
157     * at least one character of a host name (embeds Parser::EXT_LINK_URL_CLASS)
158     */
159    // phpcs:ignore Generic.Files.LineLength
160    private const EXT_LINK_ADDR = '(?:[0-9.]+|\\[(?i:[0-9a-f:.]+)\\]|[^][<>"\\x00-\\x20\\x7F\p{Zs}\x{FFFD}])';
161    /** RegExp to make image URLs (embeds IPv6 part of Parser::EXT_LINK_ADDR) */
162    // phpcs:ignore Generic.Files.LineLength
163    private const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)((?:\\[(?i:[0-9a-f:.]+)\\])?[^][<>"\\x00-\\x20\\x7F\p{Zs}\x{FFFD}]+)
164        \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)avif|gif|jpg|jpeg|png|svg|webp)$/Sxu';
165
166    /** Regular expression for a non-newline space */
167    private const SPACE_NOT_NL = '(?:\t|&nbsp;|&\#0*160;|&\#[Xx]0*[Aa]0;|\p{Zs})';
168
169    /**
170     * @var int Preprocess wikitext in transclusion mode
171     * @deprecated Since 1.36
172     */
173    public const PTD_FOR_INCLUSION = Preprocessor::DOM_FOR_INCLUSION;
174
175    # Allowed values for $this->mOutputType
176    /** Output type: like Parser::parse() */
177    public const OT_HTML = 1;
178    /** Output type: like Parser::preSaveTransform() */
179    public const OT_WIKI = 2;
180    /** Output type: like Parser::preprocess() */
181    public const OT_PREPROCESS = 3;
182    /**
183     * Output type: like Parser::extractSections() - portions of the
184     * original are returned unchanged.
185     */
186    public const OT_PLAIN = 4;
187
188    /**
189     * @var string Prefix and suffix for temporary replacement strings
190     * for the multipass parser.
191     *
192     * \x7f should never appear in input as it's disallowed in XML.
193     * Using it at the front also gives us a little extra robustness
194     * since it shouldn't match when butted up against identifier-like
195     * string constructs.
196     *
197     * Must not consist of all title characters, or else it will change
198     * the behavior of <nowiki> in a link.
199     *
200     * Must have a character that needs escaping in attributes, otherwise
201     * someone could put a strip marker in an attribute, to get around
202     * escaping quote marks, and break out of the attribute. Thus we add
203     * `'".
204     */
205    public const MARKER_SUFFIX = "-QINU`\"'\x7f";
206    public const MARKER_PREFIX = "\x7f'\"`UNIQ-";
207    private const HEADLINE_MARKER_REGEX = '/^' . self::MARKER_PREFIX . '-h-(\d+)-' . self::MARKER_SUFFIX . '/';
208
209    /**
210     * Internal marker used by parser to track where the table of
211     * contents should be. Various magic words can change the position
212     * during the parse.  The table of contents is generated during
213     * the parse, however skins have the final decision on whether the
214     * table of contents is injected.  This placeholder element
215     * identifies where in the page the table of contents should be
216     * injected, if at all.
217     * @var string
218     * @see Keep this in sync with BlockLevelPass::execute() and
219     *  RemexCompatMunger::isTableOfContentsMarker()
220     * @internal Skins should *not* directly reference TOC_PLACEHOLDER
221     * but instead use Parser::replaceTableOfContentsMarker().
222     */
223    public const TOC_PLACEHOLDER = '<meta property="mw:PageProp/toc" />';
224
225    # Persistent:
226    /** @var array<string,callable> */
227    private array $mTagHooks = [];
228    /** @var array<string,array{0:callable,1:int}> */
229    private array $mFunctionHooks = [];
230    /** @var array{0:array<string,string>,1:array<string,string>} */
231    private array $mFunctionSynonyms = [ 0 => [], 1 => [] ];
232    /** @var string[] */
233    private array $mStripList = [];
234    /** @var array<string,string> */
235    private array $mVarCache = [];
236    /** @var array<string,array<string,string[]>> */
237    private array $mImageParams = [];
238    /** @var array<string,MagicWordArray> */
239    private array $mImageParamsMagicArray = [];
240    /** @deprecated since 1.35 */
241    public $mMarkerIndex = 0;
242
243    // Initialised by initializeVariables()
244    /** @var MagicWordArray */
245    private MagicWordArray $mVariables;
246    private MagicWordArray $mSubstWords;
247
248    // Initialised in constructor
249    /** @var string */
250    private string $mExtLinkBracketedRegex;
251    private HookRunner $hookRunner;
252    private Preprocessor $mPreprocessor;
253
254    // Cleared with clearState():
255    /** @var ParserOutput */
256    private ParserOutput $mOutput;
257    private int $mAutonumber = 0;
258    private StripState $mStripState;
259    private LinkHolderArray $mLinkHolders;
260    private int $mLinkID = 0;
261    private array $mIncludeSizes;
262    /**
263     * @internal
264     * @var int
265     */
266    public $mPPNodeCount;
267    /**
268     * @internal
269     * @var int
270     */
271    public $mHighestExpansionDepth;
272    private array $mTplRedirCache;
273    /** @internal */
274    public array $mHeadings;
275    /** @var array<string,string> */
276    private array $mDoubleUnderscores;
277    /**
278     * Number of expensive parser function calls
279     * @deprecated since 1.35
280     */
281    public $mExpensiveFunctionCount;
282    private bool $mShowToc;
283    private bool $mForceTocPosition;
284    private array $mTplDomCache;
285    private ?UserIdentity $mUser;
286
287    # Temporary
288    # These are variables reset at least once per parse regardless of $clearState
289
290    /**
291     * @var ParserOptions|null
292     * @deprecated since 1.35, use Parser::getOptions()
293     */
294    private $mOptions;
295
296    # Deprecated "dynamic" properties
297    # These used to be dynamic properties added to the parser, but these
298    # have been deprecated since 1.42.
299    /** @deprecated since 1.42: T203531 */
300    public $mExtVariables;
301    /** @deprecated since 1.42: T203532 */
302    public $mExtArrays;
303    /** @deprecated since 1.42: T359887 */
304    public $mExtHashTables;
305    /** @deprecated since 1.42: T203563 */
306    public $mExtLoopsCounter;
307
308    /**
309     * Title context, used for self-link rendering and similar things
310     *
311     * @deprecated since 1.35, use Parser::getPage()
312     */
313    private Title $mTitle;
314    /** Output type, one of the OT_xxx constants */
315    private int $mOutputType;
316    /**
317     * When false (default), extension tags are put in the general strip state
318     * for OT_PREPROCESS.
319     * When true, extension tags are skipped during OT_PREPROCESS (parsoid
320     * fragment v1) or put into the `exttag` strip state (parsoid fragment
321     * v2+)
322     */
323    private bool $useParsoidFragments = false;
324    /**
325     * Shortcut alias, see Parser::setOutputType()
326     * @deprecated since 1.35
327     */
328    private array $ot;
329    /** ID to display in {{REVISIONID}} tags */
330    private ?int $mRevisionId = null;
331    /** The timestamp of the specified revision ID */
332    private ?string $mRevisionTimestamp = null;
333    /** User to display in {{REVISIONUSER}} tag */
334    private ?string $mRevisionUser = null;
335    /** Size to display in {{REVISIONSIZE}} variable */
336    private ?int $mRevisionSize = null;
337    /** @var int|false For {{PAGESIZE}} on current page */
338    private $mInputSize = false;
339
340    private ?RevisionRecord $mRevisionRecordObject = null;
341
342    /**
343     * A cache of the current revisions of titles. Keys are $title->getPrefixedDbKey()
344     *
345     * @since 1.24
346     */
347    private ?MapCacheLRU $currentRevisionCache = null;
348
349    /**
350     * @var bool|string Recursive call protection.
351     * @internal
352     */
353    private $mInParse = false;
354
355    private SectionProfiler $mProfiler;
356    private ?LinkRenderer $mLinkRenderer = null;
357
358    /**
359     * @internal For use by ServiceWiring
360     */
361    public const CONSTRUCTOR_OPTIONS = [
362        // See documentation for the corresponding config options
363        // Many of these are only used in (eg) CoreMagicVariables
364        MainConfigNames::AllowDisplayTitle,
365        MainConfigNames::AllowSlowParserFunctions,
366        MainConfigNames::ArticlePath,
367        MainConfigNames::EnableScaryTranscluding,
368        MainConfigNames::ExtraInterlanguageLinkPrefixes,
369        MainConfigNames::FragmentMode,
370        MainConfigNames::Localtimezone,
371        MainConfigNames::MaxArticleSize,
372        MainConfigNames::MaxSigChars,
373        MainConfigNames::MaxTocLevel,
374        MainConfigNames::MiserMode,
375        MainConfigNames::RawHtml,
376        MainConfigNames::ScriptPath,
377        MainConfigNames::Server,
378        MainConfigNames::ServerName,
379        MainConfigNames::ShowHostnames,
380        MainConfigNames::SignatureValidation,
381        MainConfigNames::Sitename,
382        MainConfigNames::StylePath,
383        MainConfigNames::TranscludeCacheExpiry,
384        MainConfigNames::PreprocessorCacheThreshold,
385        MainConfigNames::EnableParserLimitReporting,
386        MainConfigNames::ParserEnableUserLanguage,
387    ];
388
389    /**
390     * Constructing parsers directly is not allowed! Use a ParserFactory.
391     * @internal
392     */
393    public function __construct(
394        // This is called $svcOptions instead of $options like elsewhere to avoid confusion with
395        // $mOptions, which is public and widely used, and also with the local variable $options used
396        // for ParserOptions throughout this file.
397        private ServiceOptions $svcOptions,
398        private ParserCoreTagHooks $parserCoreTagHooks,
399        private MagicWordFactory $magicWordFactory,
400        private Language $contLang,
401        private UrlUtils $urlUtils,
402        private SpecialPageFactory $specialPageFactory,
403        private LinkRendererFactory $linkRendererFactory,
404        private NamespaceInfo $nsInfo,
405        private LoggerInterface $logger,
406        private BadFileLookup $badFileLookup,
407        private RepoGroup $repoGroup,
408        private LanguageFactory $languageFactory,
409        private LanguageConverterFactory $languageConverterFactory,
410        private LanguageNameUtils $languageNameUtils,
411        private HookContainer $hookContainer,
412        private TidyDriverBase $tidy,
413        private WANObjectCache $wanCache,
414        private UserOptionsLookup $userOptionsLookup,
415        private UserFactory $userFactory,
416        private TitleFormatter $titleFormatter,
417        private HttpRequestFactory $httpRequestFactory,
418        private TrackingCategories $trackingCategories,
419        private SignatureValidatorFactory $signatureValidatorFactory,
420        private UserNameUtils $userNameUtils,
421        private LinkAlwaysKnownLookup $linkAlwaysKnownLookup,
422    ) {
423        $this->deprecateDynamicPropertiesAccess( '1.42', __CLASS__ );
424        $this->deprecatePublicProperty( 'ot', '1.35', __CLASS__ );
425        $this->deprecatePublicProperty( 'mTitle', '1.35', __CLASS__ );
426        $this->deprecatePublicProperty( 'mOptions', '1.35', __CLASS__ );
427
428        if ( ParserFactory::$inParserFactory === 0 ) {
429            // Direct construction of Parser was deprecated in 1.34 and
430            // removed in 1.36; use a ParserFactory instead.
431            throw new BadMethodCallException( 'Direct construction of Parser not allowed' );
432        }
433        $svcOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
434
435        $this->mExtLinkBracketedRegex = '/\[(((?i)' . $this->urlUtils->validProtocols() . ')' .
436            self::EXT_LINK_ADDR .
437            self::EXT_LINK_URL_CLASS . '*)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F\\x{FFFD}]*)\]/Su';
438