Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
DeleteSelfExternals
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
2 / 2
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * Delete self-references to $wgServer from the externallinks table.
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Maintenance
8 */
9
10use MediaWiki\Deferred\LinksUpdate\ExternalLinksTable;
11use MediaWiki\ExternalLinks\LinkFilter;
12use MediaWiki\MainConfigNames;
13use MediaWiki\Maintenance\Maintenance;
14
15// @codeCoverageIgnoreStart
16require_once __DIR__ . '/Maintenance.php';
17// @codeCoverageIgnoreEnd
18
19/**
20 * Maintenance script that deletes self-references to $wgServer
21 * from the externallinks table.
22 *
23 * @ingroup Maintenance
24 */
25class DeleteSelfExternals extends Maintenance {
26    public function __construct() {
27        parent::__construct();
28        $this->addDescription( 'Delete self-references to $wgServer from externallinks' );
29        $this->setBatchSize( 1000 );
30    }
31
32    public function execute() {
33        // Extract the host and scheme from $wgServer
34        $server = $this->getConfig()->get( MainConfigNames::Server );
35        $bits = $this->getServiceContainer()->getUrlUtils()->parse( $server );
36        if ( !$bits ) {
37            $this->fatalError( 'Could not parse $wgServer' );
38        }
39
40        $this->output( "Deleting self externals from $server\n" );
41        $db = $this->getServiceContainer()->getConnectionProvider()->getPrimaryDatabase(
42            ExternalLinksTable::VIRTUAL_DOMAIN
43        );
44
45        // If it's protocol-relative, we need to do both http and https.
46        // Otherwise, just do the specified scheme.
47        $host = $bits['host'];
48        if ( isset( $bits['port'] ) ) {
49            $host .= ':' . $bits['port'];
50        }
51        if ( $bits['scheme'] != '' ) {
52            $conds = [ LinkFilter::getQueryConditions( $host, [ 'protocol' => $bits['scheme'] . '://' ] ) ];
53        } else {
54            $conds = [
55                LinkFilter::getQueryConditions( $host, [ 'protocol' => 'http://' ] ),
56                LinkFilter::getQueryConditions( $host, [ 'protocol' => 'https://' ] ),
57            ];
58        }
59
60        // Convert the array of $conds into an IExpression object for use in the DELETE query
61        // The use of array_filter is just there for a sanity check, as LinkFilter::getQueryConditions
62        // only returns false if the host was invalid (we have already validated this above).
63        $conds = array_map( static function ( $cond ) use ( $db ) {
64            return $db->andExpr( $cond );
65        }, array_filter( $conds ) );
66        $domainExpr = $db->orExpr( $conds );
67
68        $totalRows = 0;
69        $batchStart = 0;
70        $batchEnd = $batchStart + $this->getBatchSize();
71        do {
72            $this->output( "Deleting self-externals with el_id $batchStart to $batchEnd\n" );
73
74            $db->newDeleteQueryBuilder()
75                ->deleteFrom( 'externallinks' )
76                ->where( $domainExpr )
77                ->andWhere( $db->expr( 'el_id', '>', $batchStart ) )
78                ->andWhere( $db->expr( 'el_id', '<=', $batchEnd ) )
79                ->caller( __METHOD__ )
80                ->execute();
81            $rowsDeletedInThisBatch = $db->affectedRows();
82            $totalRows += $rowsDeletedInThisBatch;
83
84            $batchStart += $this->getBatchSize();
85            $batchEnd += $this->getBatchSize();
86            $this->waitForReplication();
87        } while ( $rowsDeletedInThisBatch );
88
89        $this->output( "done; deleted $totalRows rows\n" );
90    }
91}
92
93// @codeCoverageIgnoreStart
94$maintClass = DeleteSelfExternals::class;
95require_once RUN_MAINTENANCE_IF_MAIN;
96// @codeCoverageIgnoreEnd