MediaWiki  master
cleanupBlocks.php
Go to the documentation of this file.
1 <?php
24 require_once __DIR__ . '/Maintenance.php';
25 
27 
34 class CleanupBlocks extends Maintenance {
35 
36  public function __construct() {
37  parent::__construct();
38  $this->addDescription( "Cleanup user blocks with user names not matching the 'user' table" );
39  $this->setBatchSize( 1000 );
40  }
41 
42  public function execute() {
43  $db = $this->getDB( DB_MASTER );
44  $blockQuery = DatabaseBlock::getQueryInfo();
45 
46  $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' );
47 
48  // Step 1: Clean up any duplicate user blocks
49  $batchSize = $this->getBatchSize();
50  for ( $from = 1; $from <= $max; $from += $batchSize ) {
51  $to = min( $max, $from + $batchSize - 1 );
52  $this->output( "Cleaning up duplicate ipb_user ($from-$to of $max)\n" );
53 
54  $delete = [];
55 
56  $res = $db->select(
57  'ipblocks',
58  [ 'ipb_user' ],
59  [
60  "ipb_user >= " . (int)$from,
61  "ipb_user <= " . (int)$to,
62  ],
63  __METHOD__,
64  [
65  'GROUP BY' => 'ipb_user',
66  'HAVING' => 'COUNT(*) > 1',
67  ]
68  );
69  foreach ( $res as $row ) {
70  $bestBlock = null;
71  $res2 = $db->select(
72  $blockQuery['tables'],
73  $blockQuery['fields'],
74  [
75  'ipb_user' => $row->ipb_user,
76  ],
77  __METHOD__,
78  [],
79  $blockQuery['joins']
80  );
81  foreach ( $res2 as $row2 ) {
82  $block = DatabaseBlock::newFromRow( $row2 );
83  if ( !$bestBlock ) {
84  $bestBlock = $block;
85  continue;
86  }
87 
88  // Find the most-restrictive block. Can't use
89  // DatabaseBlock::chooseBlock because that's for IP blocks, not
90  // user blocks.
91  $keep = null;
92  if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) {
93  // This works for infinite blocks because 'infinity' > '20141024234513'
94  $keep = $block->getExpiry() > $bestBlock->getExpiry();
95  }
96  if ( $keep === null ) {
97  if ( $block->isCreateAccountBlocked() xor $bestBlock->isCreateAccountBlocked() ) {
98  $keep = $block->isCreateAccountBlocked();
99  } elseif ( $block->isEmailBlocked() xor $bestBlock->isEmailBlocked() ) {
100  $keep = $block->isEmailBlocked();
101  } elseif ( $block->isUsertalkEditAllowed() xor $bestBlock->isUsertalkEditAllowed() ) {
102  $keep = $block->isUsertalkEditAllowed();
103  }
104  }
105 
106  if ( $keep ) {
107  $delete[] = $bestBlock->getId();
108  $bestBlock = $block;
109  } else {
110  $delete[] = $block->getId();
111  }
112  }
113  }
114 
115  if ( $delete ) {
116  $db->delete(
117  'ipblocks',
118  [ 'ipb_id' => $delete ],
119  __METHOD__
120  );
121  }
122  }
123 
124  // Step 2: Update the user name in any blocks where it doesn't match
125  for ( $from = 1; $from <= $max; $from += $batchSize ) {
126  $to = min( $max, $from + $batchSize - 1 );
127  $this->output( "Cleaning up mismatched user name ($from-$to of $max)\n" );
128 
129  $res = $db->select(
130  [ 'ipblocks', 'user' ],
131  [ 'ipb_id', 'user_name' ],
132  [
133  'ipb_user = user_id',
134  "ipb_user >= " . (int)$from,
135  "ipb_user <= " . (int)$to,
136  'ipb_address != user_name',
137  ],
138  __METHOD__
139  );
140  foreach ( $res as $row ) {
141  $db->update(
142  'ipblocks',
143  [ 'ipb_address' => $row->user_name ],
144  [ 'ipb_id' => $row->ipb_id ],
145  __METHOD__
146  );
147  }
148  }
149 
150  $this->output( "Done!\n" );
151  }
152 }
153 
154 $maintClass = CleanupBlocks::class;
155 require_once RUN_MAINTENANCE_IF_MAIN;
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:39
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:82
setBatchSize( $s=0)
Set the batch size.
const DB_MASTER
Definition: defines.php:26
$maintClass
addDescription( $text)
Set the description text.
Maintenance script to clean up user blocks with user names not matching the &#39;user&#39; table...
output( $out, $channel=null)
Throw some output to the user.
getBatchSize()
Returns batch size.
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.