38 private $internals = [];
40 private $interwikis = [];
46 private $languageConverter;
58 $this->parent = $parent;
59 $this->languageConverter = $languageConverter;
60 $this->hookRunner =
new HookRunner( $hookContainer );
68 foreach ( $this as $name => $_ ) {
69 unset( $this->$name );
77 public function merge( $other ) {
78 foreach ( $other->internals as $ns => $entries ) {
79 $this->size += count( $entries );
80 if ( !isset( $this->internals[$ns] ) ) {
81 $this->internals[$ns] = $entries;
83 $this->internals[$ns] += $entries;
86 $this->interwikis += $other->interwikis;
94 $linkHolderBatchSize = MediaWikiServices::getInstance()->getMainConfig()
95 ->get( MainConfigNames::LinkHolderBatchSize );
96 return $this->size > $linkHolderBatchSize;
104 $this->internals = [];
105 $this->interwikis = [];
122 # Separate the link trail from the rest of the link
123 [ $inside, $trail ] = Linker::splitTrail( $trail );
125 $key = $this->parent->nextLinkID();
128 'text' => $prefix . $text . $inside,
135 $this->interwikis[$key] = $entry;
136 return "<!--IWLINK'\" $key-->{$trail}";
139 $this->internals[$ns][$key] = $entry;
140 return "<!--LINK'\" $ns:$key-->{$trail}";
159 if ( !$this->internals ) {
164 $services = MediaWikiServices::getInstance();
165 $linkCache = $services->getLinkCache();
166 $output = $this->parent->getOutput();
167 $linkRenderer = $this->parent->getLinkRenderer();
172 ksort( $this->internals );
177 $linkBatchFactory = $services->getLinkBatchFactory();
178 $lb = $linkBatchFactory->newLinkBatch();
179 $lb->setCaller( __METHOD__ );
181 foreach ( $this->internals as $ns => $entries ) {
182 foreach ( $entries as [
'title' => $title,
'pdbk' => $pdbk ] ) {
184 # Check if it's a static known link, e.g. interwiki
185 if ( $title->isAlwaysKnown() ) {
186 $classes[$pdbk] =
'';
188 $classes[$pdbk] =
'new';
190 $id = $linkCache->getGoodLinkID( $pdbk );
192 $classes[$pdbk] = $linkRenderer->getLinkClasses( $title );
193 $output->addLink( $title, $id );
194 $pagemap[$id] = $pdbk;
195 } elseif ( $linkCache->isBadLink( $pdbk ) ) {
196 $classes[$pdbk] =
'new';
198 # Not in the link cache, add it to the query
199 $lb->addObj( $title );
204 if ( !$lb->isEmpty() ) {
205 $res = $dbr->newSelectQueryBuilder()
208 ->where( [ $lb->constructSet(
'page', $dbr ) ] )
209 ->caller( __METHOD__ )
212 # Fetch data and form into an associative array
213 # non-existent = broken
214 foreach ( $res as $s ) {
215 $title = Title::makeTitle( $s->page_namespace, $s->page_title );
216 $pdbk = $title->getPrefixedDBkey();
217 $linkCache->addGoodLinkObjFromRow( $title, $s );
218 $output->addLink( $title, $s->page_id );
219 $classes[$pdbk] = $linkRenderer->getLinkClasses( $title );
221 $pagemap[$s->page_id] = $pdbk;
225 if ( $pagemap !== [] ) {
227 $this->hookRunner->onGetLinkColours( $pagemap, $classes, $this->parent->getTitle() );
230 # Do a second query for different language variants of links and categories
231 if ( $this->languageConverter->hasVariants() ) {
235 # Construct search and replace arrays
237 foreach ( $this->internals as $ns => $entries ) {
238 foreach ( $entries as $index => $entry ) {
239 $pdbk = $entry[
'pdbk'];
240 $title = $entry[
'title'];
241 $query = $entry[
'query'] ?? [];
242 $searchkey =
"$ns:$index";
243 $displayTextHtml = $entry[
'text'];
244 if ( isset( $entry[
'selflink'] ) ) {
245 $replacePairs[$searchkey] = Linker::makeSelfLinkObj( $title, $displayTextHtml, $query );
249 $displayText = $displayTextHtml ===
'' ? null :
new HtmlArmor( $displayTextHtml );
250 if ( !isset( $classes[$pdbk] ) ) {
251 $classes[$pdbk] =
'new';
253 if ( $classes[$pdbk] ===
'new' ) {
254 $linkCache->addBadLinkObj( $title );
255 $output->addLink( $title, 0 );
256 $link = $linkRenderer->makeBrokenLink(
257 $title, $displayText, [], $query
260 $link = $linkRenderer->makePreloadedLink(
261 $title, $displayText, $classes[$pdbk], [], $query
265 $replacePairs[$searchkey] = $link;
270 $text = preg_replace_callback(
271 '/<!--LINK\'" (-?[\d+:]+)-->/',
272 static function ( array
$matches ) use ( $replacePairs ) {
284 if ( !$this->interwikis ) {
288 # Make interwiki link HTML
289 $output = $this->parent->getOutput();
291 $linkRenderer = $this->parent->getLinkRenderer();
292 foreach ( $this->interwikis as $key => [
'title' => $title,
'text' => $linkText ] ) {
293 $replacePairs[$key] = $linkRenderer->makeLink( $title,
new HtmlArmor( $linkText ) );
294 $output->addInterwikiLink( $title );
297 $text = preg_replace_callback(
298 '/<!--IWLINK\'" (\d+)-->/',
299 static function ( array
$matches ) use ( $replacePairs ) {
311 $linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
312 $linkBatch = $linkBatchFactory->newLinkBatch();
314 $output = $this->parent->getOutput();
315 $titlesToBeConverted =
'';
321 foreach ( $this->internals as $ns => $entries ) {
325 foreach ( $entries as $index => [
'title' => $title,
'pdbk' => $pdbk ] ) {
327 if ( !isset( $classes[$pdbk] ) || $classes[$pdbk] ===
'new' ) {
328 $titlesAttrs[] = [ $index, $title ];
331 $titlesToBeConverted .= $title->getText() .
"\0";
337 $titlesAllVariants = $this->languageConverter->
338 autoConvertToAllVariants( rtrim( $titlesToBeConverted,
"\0" ) );
339 foreach ( $titlesAllVariants as &$titlesVariant ) {
340 $titlesVariant = explode(
"\0", $titlesVariant );
344 $parentTitle = $this->parent->getTitle();
345 foreach ( $titlesAttrs as $i => [ $index, $title ] ) {
347 $ns = $title->getNamespace();
348 $text = $title->getText();
350 foreach ( $titlesAllVariants as $textVariants ) {
351 $textVariant = $textVariants[$i];
352 if ( $textVariant === $text ) {
356 $variantTitle = Title::makeTitle( $ns, $textVariant );
361 if ( $variantTitle->equals( $parentTitle ) && !$title->hasFragment() ) {
362 $this->internals[$ns][$index][
'selflink'] =
true;
366 $linkBatch->addObj( $variantTitle );
367 $variantMap[$variantTitle->getPrefixedDBkey()][] =
"$ns:$index";
373 foreach ( $output->getCategoryNames() as $category ) {
374 $categoryTitle = Title::makeTitleSafe(
NS_CATEGORY, $category );
375 $linkBatch->addObj( $categoryTitle );
376 $variants = $this->languageConverter->autoConvertToAllVariants( $category );
377 foreach ( $variants as $variant ) {
378 if ( $variant !== $category ) {
379 $variantTitle = Title::makeTitleSafe(
NS_CATEGORY, $variant );
380 if ( $variantTitle ) {
381 $linkBatch->addObj( $variantTitle );
382 $categoryMap[$variant] = [ $category, $categoryTitle ];
388 if ( $linkBatch->isEmpty() ) {
395 $varRes = $dbr->newSelectQueryBuilder()
398 ->where( [ $linkBatch->constructSet(
'page', $dbr ) ] )
399 ->caller( __METHOD__ )
404 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
405 $linkRenderer = $this->parent->getLinkRenderer();
408 foreach ( $varRes as $s ) {
409 $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title );
410 $varPdbk = $variantTitle->getPrefixedDBkey();
412 if ( !isset( $variantMap[$varPdbk] ) ) {
416 $linkCache->addGoodLinkObjFromRow( $variantTitle, $s );
417 $output->addLink( $variantTitle, $s->page_id );
420 foreach ( $variantMap[$varPdbk] as $key ) {
421 [ $ns, $index ] = explode(
':', $key, 2 );
422 $entry =& $this->internals[(int)$ns][(
int)$index];
423 $pdbk = $entry[
'pdbk'];
425 if ( !isset( $classes[$pdbk] ) || $classes[$pdbk] ===
'new' ) {
427 $entry[
'title'] = $variantTitle;
428 $entry[
'pdbk'] = $varPdbk;
431 if ( !isset( $classes[$varPdbk] ) ) {
432 $classes[$varPdbk] = $linkRenderer->getLinkClasses( $variantTitle );
433 $pagemap[$s->page_id] = $varPdbk;
439 $vardbk = $variantTitle->getDBkey();
440 if ( isset( $categoryMap[$vardbk] ) ) {
441 [ $oldkey, $oldtitle ] = $categoryMap[$vardbk];
442 if ( !isset( $varCategories[$oldkey] ) && !$oldtitle->exists() ) {
443 $varCategories[$oldkey] = $vardbk;
447 $this->hookRunner->onGetLinkColours( $pagemap, $classes, $this->parent->getTitle() );
450 if ( $varCategories !== [] ) {
452 foreach ( $output->getCategoryNames() as $cat ) {
453 $sortkey = $output->getCategorySortKey( $cat );
455 $newCats[$varCategories[$cat] ?? $cat] = $sortkey;
457 $output->setCategories( $newCats );
469 return preg_replace_callback(
470 '/<!--(IW)?LINK\'" (-?[\d:]+)-->/',
472 [ $unchanged, $isInterwiki, $key ] =
$matches;
474 if ( !$isInterwiki ) {
475 [ $ns, $index ] = explode(
':', $key, 2 );
476 return $this->internals[(int)$ns][(
int)$index][
'text'] ?? $unchanged;
478 return $this->interwikis[$key][
'text'] ?? $unchanged;
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Marks HTML that shouldn't be escaped.
static getSelectFields()
Fields that LinkCache needs to select.
doVariants(&$classes)
Modify $this->internals and $classes according to language variant linking rules.
replaceInterwiki(&$text)
Replace interwiki links.
merge( $other)
Merge another LinkHolderArray into this one.
replaceText( $text)
Replace and link placeholders with plain text of links (not HTML-formatted).
replaceInternal(&$text)
Replace internal links.
__destruct()
Reduce memory usage to reduce the impact of circular references.
__construct(Parser $parent, ILanguageConverter $languageConverter, HookContainer $hookContainer)
clear()
Clear all stored link holders.
isBig()
Returns true if the memory requirements of this object are getting large.
makeHolder(Title $nt, $text='', $trail='', $prefix='')
Make a link placeholder.
replace(&$text)
Replace link placeholders with actual links, in the buffer.
A class containing constants representing the names of configuration variables.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
The shared interface for all language converters.