Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
DeleteSelfExternals
100.00% covered (success)
100.00%
40 / 40
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%
37 / 37
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * Delete self-references to $wgServer from the externallinks table.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Maintenance
22 */
23
24use MediaWiki\ExternalLinks\LinkFilter;
25use MediaWiki\MainConfigNames;
26use MediaWiki\Maintenance\Maintenance;
27
28// @codeCoverageIgnoreStart
29require_once __DIR__ . '/Maintenance.php';
30// @codeCoverageIgnoreEnd
31
32/**
33 * Maintenance script that deletes self-references to $wgServer
34 * from the externallinks table.
35 *
36 * @ingroup Maintenance
37 */
38class DeleteSelfExternals extends Maintenance {
39    public function __construct() {
40        parent::__construct();
41        $this->addDescription( 'Delete self-references to $wgServer from externallinks' );
42        $this->setBatchSize( 1000 );
43    }
44
45    public function execute() {
46        // Extract the host and scheme from $wgServer
47        $server = $this->getConfig()->get( MainConfigNames::Server );
48        $bits = $this->getServiceContainer()->getUrlUtils()->parse( $server );
49        if ( !$bits ) {
50            $this->fatalError( 'Could not parse $wgServer' );
51        }
52
53        $this->output( "Deleting self externals from $server\n" );
54        $db = $this->getPrimaryDB();
55
56        // If it's protocol-relative, we need to do both http and https.
57        // Otherwise, just do the specified scheme.
58        $host = $bits['host'];
59        if ( isset( $bits['port'] ) ) {
60            $host .= ':' . $bits['port'];
61        }
62        if ( $bits['scheme'] != '' ) {
63            $conds = [ LinkFilter::getQueryConditions( $host, [ 'protocol' => $bits['scheme'] . '://' ] ) ];
64        } else {
65            $conds = [
66                LinkFilter::getQueryConditions( $host, [ 'protocol' => 'http://' ] ),
67                LinkFilter::getQueryConditions( $host, [ 'protocol' => 'https://' ] ),
68            ];
69        }
70
71        // Convert the array of $conds into an IExpression object for use in the DELETE query
72        // The use of array_filter is just there for a sanity check, as LinkFilter::getQueryConditions
73        // only returns false if the host was invalid (we have already validated this above).
74        $conds = array_map( static function ( $cond ) use ( $db ) {
75            return $db->andExpr( $cond );
76        }, array_filter( $conds ) );
77        $domainExpr = $db->orExpr( $conds );
78
79        $totalRows = 0;
80        $batchStart = 0;
81        $batchEnd = $batchStart + $this->getBatchSize();
82        do {
83            $this->output( "Deleting self-externals with el_id $batchStart to $batchEnd\n" );
84
85            $db->newDeleteQueryBuilder()
86                ->deleteFrom( 'externallinks' )
87                ->where( $domainExpr )
88                ->andWhere( $db->expr( 'el_id', '>', $batchStart ) )
89                ->andWhere( $db->expr( 'el_id', '<=', $batchEnd ) )
90                ->caller( __METHOD__ )
91                ->execute();
92            $rowsDeletedInThisBatch = $db->affectedRows();
93            $totalRows += $rowsDeletedInThisBatch;
94
95            $batchStart += $this->getBatchSize();
96            $batchEnd += $this->getBatchSize();
97            $this->waitForReplication();
98        } while ( $rowsDeletedInThisBatch );
99
100        $this->output( "done; deleted $totalRows rows\n" );
101    }
102}
103
104// @codeCoverageIgnoreStart
105$maintClass = DeleteSelfExternals::class;
106require_once RUN_MAINTENANCE_IF_MAIN;
107// @codeCoverageIgnoreEnd