53 private $languageConverter;
69 $this->languageConverter = $languageConverter;
70 $this->hookRunner =
new HookRunner( $hookContainer );
78 foreach ( $this as $name => $_ ) {
79 unset( $this->$name );
87 public function merge( $other ) {
88 foreach ( $other->internals as $ns => $entries ) {
89 $this->size += count( $entries );
90 if ( !isset( $this->internals[$ns] ) ) {
91 $this->internals[$ns] = $entries;
93 $this->internals[$ns] += $entries;
96 $this->interwikis += $other->interwikis;
104 $linkHolderBatchSize = MediaWikiServices::getInstance()->getMainConfig()
105 ->get( MainConfigNames::LinkHolderBatchSize );
106 return $this->size > $linkHolderBatchSize;
114 $this->internals = [];
115 $this->interwikis = [];
132 # Separate the link trail from the rest of the link
133 [ $inside, $trail ] = Linker::splitTrail( $trail );
135 $key = $this->parent->nextLinkID();
138 'text' => $prefix . $text . $inside,
145 $this->interwikis[$key] = $entry;
146 return "<!--IWLINK'\" $key-->{$trail}";
149 $this->internals[$ns][$key] = $entry;
150 return "<!--LINK'\" $ns:$key-->{$trail}";
169 if ( !$this->internals ) {
174 $services = MediaWikiServices::getInstance();
175 $linkCache = $services->getLinkCache();
176 $output = $this->parent->getOutput();
177 $linkRenderer = $this->parent->getLinkRenderer();
182 ksort( $this->internals );
187 $linkBatchFactory = $services->getLinkBatchFactory();
188 $lb = $linkBatchFactory->newLinkBatch();
189 $lb->setCaller( __METHOD__ );
191 foreach ( $this->internals as $ns => $entries ) {
192 foreach ( $entries as [
'title' =>
$title,
'pdbk' => $pdbk ] ) {
194 # Check if it's a static known link, e.g. interwiki
195 if (
$title->isAlwaysKnown() ) {
196 $classes[$pdbk] =
'';
198 $classes[$pdbk] =
'new';
200 $id = $linkCache->getGoodLinkID( $pdbk );
202 $classes[$pdbk] = $linkRenderer->getLinkClasses(
$title );
203 $output->addLink(
$title, $id );
204 $pagemap[$id] = $pdbk;
205 } elseif ( $linkCache->isBadLink( $pdbk ) ) {
206 $classes[$pdbk] =
'new';
208 # Not in the link cache, add it to the query
214 if ( !$lb->isEmpty() ) {
215 $res =
$dbr->newSelectQueryBuilder()
218 ->where( [ $lb->constructSet(
'page',
$dbr ) ] )
219 ->caller( __METHOD__ )
222 # Fetch data and form into an associative array
223 # non-existent = broken
225 $title = Title::makeTitle(
$s->page_namespace,
$s->page_title );
226 $pdbk =
$title->getPrefixedDBkey();
227 $linkCache->addGoodLinkObjFromRow(
$title,
$s );
228 $output->addLink(
$title,
$s->page_id );
229 $classes[$pdbk] = $linkRenderer->getLinkClasses(
$title );
231 $pagemap[
$s->page_id] = $pdbk;
235 if ( $pagemap !== [] ) {
237 $this->hookRunner->onGetLinkColours( $pagemap, $classes, $this->parent->getTitle() );
240 # Do a second query for different language variants of links and categories
241 if ( $this->languageConverter->hasVariants() ) {
245 # Construct search and replace arrays
247 foreach ( $this->internals as $ns => $entries ) {
248 foreach ( $entries as $index => $entry ) {
249 $pdbk = $entry[
'pdbk'];
251 $query = $entry[
'query'] ?? [];
252 $searchkey =
"$ns:$index";
253 $displayTextHtml = $entry[
'text'];
254 if ( isset( $entry[
'selflink'] ) ) {
255 $replacePairs[$searchkey] = Linker::makeSelfLinkObj(
$title, $displayTextHtml, $query );
259 $displayText = $displayTextHtml ===
'' ? null :
new HtmlArmor( $displayTextHtml );
260 if ( !isset( $classes[$pdbk] ) ) {
261 $classes[$pdbk] =
'new';
263 if ( $classes[$pdbk] ===
'new' ) {
264 $linkCache->addBadLinkObj(
$title );
265 $output->addLink(
$title, 0 );
266 $link = $linkRenderer->makeBrokenLink(
267 $title, $displayText, [], $query
270 $link = $linkRenderer->makePreloadedLink(
271 $title, $displayText, $classes[$pdbk], [], $query
275 $replacePairs[$searchkey] = $link;
280 $text = preg_replace_callback(
281 '/<!--LINK\'" (-?[\d+:]+)-->/',
282 static function ( array
$matches ) use ( $replacePairs ) {
294 if ( !$this->interwikis ) {
298 # Make interwiki link HTML
299 $output = $this->parent->getOutput();
301 $linkRenderer = $this->parent->getLinkRenderer();
302 foreach ( $this->interwikis as $key => [
'title' =>
$title,
'text' => $linkText ] ) {
303 $replacePairs[$key] = $linkRenderer->makeLink(
$title,
new HtmlArmor( $linkText ) );
304 $output->addInterwikiLink(
$title );
307 $text = preg_replace_callback(
308 '/<!--IWLINK\'" (\d+)-->/',
309 static function ( array
$matches ) use ( $replacePairs ) {
321 $linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
322 $linkBatch = $linkBatchFactory->newLinkBatch();
324 $output = $this->parent->getOutput();
325 $titlesToBeConverted =
'';
331 foreach ( $this->internals as $ns => $entries ) {
335 foreach ( $entries as $index => [
'title' =>
$title,
'pdbk' => $pdbk ] ) {
337 if ( !isset( $classes[$pdbk] ) || $classes[$pdbk] ===
'new' ) {
338 $titlesAttrs[] = [ $index,
$title ];
341 $titlesToBeConverted .=
$title->getText() .
"\0";
347 $titlesAllVariants = $this->languageConverter->
348 autoConvertToAllVariants( rtrim( $titlesToBeConverted,
"\0" ) );
349 foreach ( $titlesAllVariants as &$titlesVariant ) {
350 $titlesVariant = explode(
"\0", $titlesVariant );
354 $parentTitle = $this->parent->getTitle();
355 foreach ( $titlesAttrs as $i => [ $index,
$title ] ) {
357 $ns =
$title->getNamespace();
358 $text =
$title->getText();
360 foreach ( $titlesAllVariants as $textVariants ) {
361 $textVariant = $textVariants[$i];
362 if ( $textVariant === $text ) {
366 $variantTitle = Title::makeTitle( $ns, $textVariant );
371 if ( $variantTitle->equals( $parentTitle ) && !
$title->hasFragment() ) {
372 $this->internals[$ns][$index][
'selflink'] =
true;
376 $linkBatch->addObj( $variantTitle );
377 $variantMap[$variantTitle->getPrefixedDBkey()][] =
"$ns:$index";
383 foreach ( $output->getCategoryNames() as $category ) {
384 $categoryTitle = Title::makeTitleSafe(
NS_CATEGORY, $category );
385 $linkBatch->addObj( $categoryTitle );
386 $variants = $this->languageConverter->autoConvertToAllVariants( $category );
387 foreach ( $variants as $variant ) {
388 if ( $variant !== $category ) {
389 $variantTitle = Title::makeTitleSafe(
NS_CATEGORY, $variant );
390 if ( $variantTitle ) {
391 $linkBatch->addObj( $variantTitle );
392 $categoryMap[$variant] = [ $category, $categoryTitle ];
398 if ( $linkBatch->isEmpty() ) {
405 $varRes =
$dbr->newSelectQueryBuilder()
408 ->where( [ $linkBatch->constructSet(
'page',
$dbr ) ] )
409 ->caller( __METHOD__ )
414 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
415 $linkRenderer = $this->parent->getLinkRenderer();
418 foreach ( $varRes as
$s ) {
419 $variantTitle = Title::makeTitle(
$s->page_namespace,
$s->page_title );
420 $varPdbk = $variantTitle->getPrefixedDBkey();
422 if ( !isset( $variantMap[$varPdbk] ) ) {
426 $linkCache->addGoodLinkObjFromRow( $variantTitle,
$s );
427 $output->addLink( $variantTitle,
$s->page_id );
430 foreach ( $variantMap[$varPdbk] as $key ) {
431 [ $ns, $index ] = explode(
':', $key, 2 );
432 $entry =& $this->internals[(int)$ns][(
int)$index];
433 $pdbk = $entry[
'pdbk'];
435 if ( !isset( $classes[$pdbk] ) || $classes[$pdbk] ===
'new' ) {
437 $entry[
'title'] = $variantTitle;
438 $entry[
'pdbk'] = $varPdbk;
441 $classes[$varPdbk] = $linkRenderer->getLinkClasses( $variantTitle );
442 $pagemap[
$s->page_id] = $pdbk;
447 $vardbk = $variantTitle->getDBkey();
448 if ( isset( $categoryMap[$vardbk] ) ) {
449 [ $oldkey, $oldtitle ] = $categoryMap[$vardbk];
450 if ( !isset( $varCategories[$oldkey] ) && !$oldtitle->exists() ) {
451 $varCategories[$oldkey] = $vardbk;
455 $this->hookRunner->onGetLinkColours( $pagemap, $classes, $this->parent->getTitle() );
458 if ( $varCategories !== [] ) {
460 $originalCats = $output->getCategories();
461 foreach ( $originalCats as $cat => $sortkey ) {
463 $newCats[$varCategories[$cat] ?? $cat] = $sortkey;
465 $output->setCategories( $newCats );
477 return preg_replace_callback(
478 '/<!--(IW)?LINK\'" (-?[\d:]+)-->/',
480 [ $unchanged, $isInterwiki, $key ] =
$matches;
482 if ( !$isInterwiki ) {
483 [ $ns, $index ] = explode(
':', $key, 2 );
484 return $this->internals[(int)$ns][(
int)$index][
'text'] ?? $unchanged;
486 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).
array< int, array< int, array > > $internals
Indexed by numeric namespace and link ids, {.
replaceInternal(&$text)
Replace internal links.
__destruct()
Reduce memory usage to reduce the impact of circular references.
__construct(Parser $parent, ILanguageConverter $languageConverter, HookContainer $hookContainer)
array< int, array > $interwikis
Indexed by numeric link id.
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.
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s