Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.13% covered (success)
95.13%
215 / 226
81.82% covered (warning)
81.82%
9 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
LinkHolderArray
95.56% covered (success)
95.56%
215 / 225
81.82% covered (warning)
81.82%
9 / 11
64
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __destruct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 merge
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 isBig
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 clear
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 makeHolder
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
 replace
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 replaceInternal
98.77% covered (success)
98.77%
80 / 81
0.00% covered (danger)
0.00%
0 / 1
19
 replaceInterwiki
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 doVariants
89.66% covered (warning)
89.66%
78 / 87
0.00% covered (danger)
0.00%
0 / 1
29.93
 replaceText
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Holder of replacement pairs for wiki links
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Parser
8 */
9
10namespace MediaWiki\Parser;
11
12use MediaWiki\HookContainer\HookContainer;
13use MediaWiki\HookContainer\HookRunner;
14use MediaWiki\Language\ILanguageConverter;
15use MediaWiki\Linker\Linker;
16use MediaWiki\MainConfigNames;
17use MediaWiki\MediaWikiServices;
18use MediaWiki\Page\LinkCache;
19use MediaWiki\Title\Title;
20use Wikimedia\HtmlArmor\HtmlArmor;
21
22/**
23 * @internal for using in Parser only.
24 *
25 * @ingroup Parser
26 */
27class LinkHolderArray {
28    /** @var array<int,array<int,array>> Indexed by numeric namespace and link ids, {@see Parser::nextLinkID} */
29    private $internals = [];
30    /** @var array<int,array> Indexed by numeric link id */
31    private $interwikis = [];
32    /** @var int */
33    private $size = 0;
34    private HookRunner $hookRunner;
35
36    public function __construct(
37        private Parser $parent,
38        private ILanguageConverter $languageConverter,
39        HookContainer $hookContainer,
40    ) {
41        $this->hookRunner = new HookRunner( $hookContainer );
42    }
43
44    /**
45     * Reduce memory usage to reduce the impact of circular references
46     */
47    public function __destruct() {
48        // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach
49        foreach ( $this as $name => $_ ) {
50            unset( $this->$name );
51        }
52    }
53
54    /**
55     * Merge another LinkHolderArray into this one
56     * @param LinkHolderArray $other
57     */
58    public function merge( $other ) {
59        foreach ( $other->internals as $ns => $entries ) {
60            $this->size += count( $entries );
61            if ( !isset( $this->internals[$ns] ) ) {
62                $this->internals[$ns] = $entries;
63            } else {
64                $this->internals[$ns] += $entries;
65            }
66        }
67        $this->interwikis += $other->interwikis;
68    }
69
70    /**
71     * Returns true if the memory requirements of this object are getting large
72     * @return bool
73     */
74    public function isBig() {
75        $linkHolderBatchSize = MediaWikiServices::getInstance()->getMainConfig()
76            ->get( MainConfigNames::LinkHolderBatchSize );
77        return $this->size > $linkHolderBatchSize;
78    }
79
80    /**
81     * Clear all stored link holders.
82     * Make sure you don't have any text left using these link holders, before you call this
83     */
84    public function clear() {
85        $this->internals = [];
86        $this->interwikis = [];
87        $this->size = 0;
88    }
89
90    /**
91     * Make a link placeholder. The text returned can be later resolved to a real link with
92     * replaceLinkHolders(). This is done for two reasons: firstly to avoid further
93     * parsing of interwiki links, and secondly to allow all existence checks and
94     * article length checks (for stub links) to be bundled into a single query.
95     *
96     * @param Title $nt
97     * @param string $text
98     * @param string $trail [optional]
99     * @param string $prefix [optional]
100     * @return string
101     */
102    public function makeHolder( Title $nt, $text = '', $trail = '', $prefix = '' ) {
103        # Separate the link trail from the rest of the link
104        [ $inside, $trail ] = Linker::splitTrail( $trail );
105
106        $key = $this->parent->nextLinkID();
107        $entry = [
108            'title' => $nt,
109            'text' => $prefix . $text . $inside,
110            'pdbk' => $nt->getPrefixedDBkey(),
111        ];
112
113        $this->size++;
114        if ( $nt->isExternal() ) {
115            // Use a globally unique ID to keep the objects mergeable
116            $this->interwikis[$key] = $entry;
117            return "<!--IWLINK'\" $key-->{$trail}";
118        } else {
119            $ns = $nt->getNamespace();
120            $this->internals[$ns][$key] = $entry;
121            return "<!--LINK'\" $ns:$key-->{$trail}";
122        }
123    }
124
125    /**
126     * Replace <!--LINK--> link placeholders with actual links, in the buffer
127     *
128     * @param string &$text
129     */
130    public function replace( &$text ) {
131        $this->replaceInternal( $text );
132        $this->replaceInterwiki( $text );
133    }
134
135    /**
136     * Replace internal links
137     * @param string &$text
138     */
139    protected function replaceInternal( &$text ) {
140        if ( !$this->internals ) {
141            return;
142        }
143
144        $classes = [];
145        $services = MediaWikiServices::getInstance();
146        $linkCache = $services->getLinkCache();
147        $output = $this->parent->getOutput();
148        $linkRenderer = $this->parent->getLinkRenderer();
149
150        $dbr = $services->getConnectionProvider()->getReplicaDatabase();
151
152        # Sort by namespace