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
188 lines
4.7 KiB
PHP
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;
|
|
}
|
|
}
|
|
}
|
|
}
|