Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
RemoveInvalidEmails
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
2 / 2
8
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%
38 / 38
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3use MediaWiki\Maintenance\Maintenance;
4use MediaWiki\Parser\Sanitizer;
5use MediaWiki\User\User;
6
7// @codeCoverageIgnoreStart
8require_once __DIR__ . '/Maintenance.php';
9// @codeCoverageIgnoreEnd
10
11/**
12 * A script to remove emails that are invalid from
13 * the user_email column of the user table. Emails
14 * are validated before users can add them, but
15 * this was not always the case so older users may
16 * have invalid ones.
17 *
18 * By default it does a dry-run, pass --commit
19 * to actually update the database.
20 */
21class RemoveInvalidEmails extends Maintenance {
22
23    /** @var bool */
24    private $commit = false;
25
26    public function __construct() {
27        parent::__construct();
28        $this->addOption( 'commit', 'Whether to actually update the database', false, false );
29        $this->setBatchSize( 500 );
30    }
31
32    public function execute() {
33        $this->commit = $this->hasOption( 'commit' );
34        $dbr = $this->getReplicaDB();
35        $dbw = $this->getPrimaryDB();
36        $lastId = 0;
37        do {
38            $rows = $dbr->newSelectQueryBuilder()
39                ->select( [ 'user_id', 'user_email' ] )
40                ->from( 'user' )
41                ->where( [
42                    $dbr->expr( 'user_id', '>', $lastId ),
43                    $dbr->expr( 'user_email', '!=', '' ),
44                    'user_email_authenticated' => null,
45                ] )
46                ->limit( $this->getBatchSize() )
47                ->caller( __METHOD__ )->fetchResultSet();
48            $count = $rows->numRows();
49            $badIds = [];
50            foreach ( $rows as $row ) {
51                if ( !Sanitizer::validateEmail( trim( $row->user_email ) ) ) {
52                    $this->output( "Found bad email: {$row->user_email} for user #{$row->user_id}\n" );
53                    $badIds[] = $row->user_id;
54                }
55                if ( $row->user_id > $lastId ) {
56                    $lastId = $row->user_id;
57                }
58            }
59
60            if ( $badIds ) {
61                $badCount = count( $badIds );
62                if ( $this->commit ) {
63                    $this->output( "Removing $badCount emails from the database.\n" );
64                    $dbw->newUpdateQueryBuilder()
65                        ->update( 'user' )
66                        ->set( [ 'user_email' => '' ] )
67                        ->where( [ 'user_id' => $badIds ] )
68                        ->caller( __METHOD__ )
69                        ->execute();
70                    foreach ( $badIds as $badId ) {
71                        User::newFromId( $badId )->invalidateCache();
72                    }
73                    $this->waitForReplication();
74                } else {
75                    $this->output( "Would have removed $badCount emails from the database.\n" );
76
77                }
78            }
79        } while ( $count !== 0 );
80        $this->output( "Done.\n" );
81    }
82}
83
84// @codeCoverageIgnoreStart
85$maintClass = RemoveInvalidEmails::class;
86require_once RUN_MAINTENANCE_IF_MAIN;
87// @codeCoverageIgnoreEnd