wiki.techinc.nl/includes/deferred/LinksUpdate/GenericPageLinksTable.php
James D. Forrester ad06527fb4 Reorg: Namespace the Title class
This is moderately messy.

Process was principally:

* xargs rg --files-with-matches '^use Title;' | grep 'php$' | \
  xargs -P 1 -n 1 sed -i -z 's/use Title;/use MediaWiki\\Title\\Title;/1'
* rg --files-without-match 'MediaWiki\\Title\\Title;' . | grep 'php$' | \
  xargs rg --files-with-matches 'Title\b' | \
  xargs -P 1 -n 1 sed -i -z 's/\nuse /\nuse MediaWiki\\Title\\Title;\nuse /1'
* composer fix

Then manual fix-ups for a few files that don't have any use statements.

Bug: T166010
Follows-Up: Ia5d8cb759dc3bc9e9bbe217d0fb109e2f8c4101a
Change-Id: If8fc9d0d95fc1a114021e282a706fc3e7da3524b
2023-03-02 08:46:53 -05:00

188 lines
4.7 KiB
PHP

<?php
namespace MediaWiki\Deferred\LinksUpdate;
use MediaWiki\DAO\WikiAwareEntity;
use MediaWiki\Page\PageReferenceValue;
use MediaWiki\Title\Title;
use Wikimedia\Rdbms\IResultWrapper;
/**
* Shared code for pagelinks and templatelinks. They are very similar tables
* since they both link to an arbitrary page identified by namespace and title.
*
* Link ID format: string[]:
* - 0: namespace ID
* - 1: title DB key
*
* @since 1.38
*/
abstract class GenericPageLinksTable extends TitleLinksTable {
/**
* A 2d array representing the new links, with the namespace ID in the
* first key, the DB key in the second key, and the value arbitrary.
*
* @var array
*/
protected $newLinks = [];
/**
* The existing links in the same format as self::$newLinks, or null if it
* has not been loaded yet.
*
* @var array|null
*/
private $existingLinks;
/**
* Get the namespace field name
*
* @return string
*/
abstract protected function getNamespaceField();
/**
* Get the title (DB key) field name
*
* @return string
*/
abstract protected function getTitleField();
/**
* Get the link target id (DB key) field name
*
* @return string
*/
abstract protected function getTargetIdField();
/**
* @return mixed
*/
abstract protected function getFromNamespaceField();
protected function getExistingFields() {
if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
return [
'ns' => $this->getNamespaceField(),
'title' => $this->getTitleField()
];
}
return [
'ns' => 'lt_namespace',
'title' => 'lt_title',
];
}
/**
* Get existing links as an associative array
*
* @return array
*/
private function getExistingLinks() {
if ( $this->existingLinks === null ) {
$this->existingLinks = [];
foreach ( $this->fetchExistingRows() as $row ) {
$this->existingLinks[$row->ns][$row->title] = 1;
}
}
return $this->existingLinks;
}
protected function fetchExistingRows(): IResultWrapper {
$queryBuilder = $this->getDB()->newSelectQueryBuilder()
->select( $this->getExistingFields() )
->from( $this->getTableName() )
->where( $this->getFromConds() );
// This read is for updating, it's conceptually better to use the write config
if ( !( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) ) {
$queryBuilder->join( 'linktarget', null, [ $this->getTargetIdField() . '=lt_id' ] );
}
return $queryBuilder
->caller( __METHOD__ )
->fetchResultSet();
}
protected function getNewLinkIDs() {
foreach ( $this->newLinks as $ns => $links ) {
foreach ( $links as $dbk => $unused ) {
yield [ $ns, (string)$dbk ];
}
}
}
protected function getExistingLinkIDs() {
foreach ( $this->getExistingLinks() as $ns => $links ) {
foreach ( $links as $dbk => $unused ) {
yield [ $ns, (string)$dbk ];
}
}
}
protected function isExisting( $linkId ) {
[ $ns, $dbk ] = $linkId;
return isset( $this->getExistingLinks()[$ns][$dbk] );
}
protected function isInNewSet( $linkId ) {
[ $ns, $dbk ] = $linkId;
return isset( $this->newLinks[$ns][$dbk] );
}
protected function insertLink( $linkId ) {
$row = [
$this->getFromNamespaceField() => $this->getSourcePage()->getNamespace(),
];
if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
$row[$this->getNamespaceField()] = $linkId[0];
$row[$this->getTitleField()] = $linkId[1];
}
if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_NEW ) {
$row[$this->getTargetIdField()] = $this->linkTargetLookup->acquireLinkTargetId(
$this->makeTitle( $linkId ),
$this->getDB()
);
}
$this->insertRow( $row );
}
protected function deleteLink( $linkId ) {
if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
$this->deleteRow( [
$this->getNamespaceField() => $linkId[0],
$this->getTitleField() => $linkId[1]
] );
} elseif ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_NEW ) {
$this->deleteRow( [
$this->getTargetIdField() => $this->linkTargetLookup->acquireLinkTargetId(
$this->makeTitle( $linkId ),
$this->getDB()
)
] );
}
}
protected function needForcedLinkRefresh() {
return $this->isCrossNamespaceMove();
}
protected function makePageReferenceValue( $linkId ): PageReferenceValue {
return new PageReferenceValue( $linkId[0], $linkId[1], WikiAwareEntity::LOCAL );
}
protected function makeTitle( $linkId ): Title {
return Title::makeTitle( $linkId[0], $linkId[1] );
}
protected function deduplicateLinkIds( $linkIds ) {
$seen = [];
foreach ( $linkIds as $linkId ) {
if ( !isset( $seen[$linkId[0]][$linkId[1]] ) ) {
$seen[$linkId[0]][$linkId[1]] = true;
yield $linkId;
}
}
}
}