MediaWiki master
cleanupPreferences.php
Go to the documentation of this file.
1<?php
13// @codeCoverageIgnoreStart
14require_once __DIR__ . '/Maintenance.php';
15// @codeCoverageIgnoreEnd
16
23
30 public function __construct() {
31 parent::__construct();
32 $this->addDescription( 'Clean up hidden preferences or removed preferences' );
33 $this->setBatchSize( 50 );
34 $this->addOption( 'dry-run', 'Print debug info instead of actually deleting' );
35 $this->addOption( 'hidden', 'Drop hidden preferences ($wgHiddenPrefs)' );
36 $this->addOption( 'unknown',
37 'Drop unknown preferences (not in $wgDefaultUserOptions or prefixed with "userjs-")' );
38 }
39
48 public function execute() {
49 $dbr = $this->getReplicaDB();
50 $hidden = $this->hasOption( 'hidden' );
51 $unknown = $this->hasOption( 'unknown' );
52
53 if ( !$hidden && !$unknown ) {
54 $this->output( "Did not select one of --hidden, --unknown, exiting\n" );
55 return;
56 }
57
58 // Remove hidden prefs. Iterate over them to avoid the IN on a large table
59 if ( $hidden ) {
60 $hiddenPrefs = $this->getConfig()->get( MainConfigNames::HiddenPrefs );
61 if ( !$hiddenPrefs ) {
62 $this->output( "No hidden preferences, skipping\n" );
63 }
64 foreach ( $hiddenPrefs as $hiddenPref ) {
65 $this->deleteByWhere(
66 $dbr,
67 'Dropping hidden preferences',
68 [ 'up_property' => $hiddenPref ]
69 );
70 }
71 }
72
73 // Remove unknown preferences. Special-case 'userjs-' as we can't control those names.
74 if ( $unknown ) {
75 $defaultUserOptions = $this->getServiceContainer()->getUserOptionsLookup()->getDefaultOptions( null );
76 $where = [
77 $dbr->expr( 'up_property', IExpression::NOT_LIKE,
78 new LikeValue( 'userjs-', $dbr->anyString() ) ),
79 $dbr->expr( 'up_property', IExpression::NOT_LIKE,
80 new LikeValue( UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX, $dbr->anyString() ) ),
81 $dbr->expr( 'up_property', '!=', array_keys( $defaultUserOptions ) ),
82 ];
83 // Allow extensions to add to the where clause to prevent deletion of their own prefs.
84 $this->getHookRunner()->onDeleteUnknownPreferences( $where, $dbr );
85 $this->deleteByWhere( $dbr, 'Dropping unknown preferences', $where );
86 }
87 }
88
89 private function deleteByWhere( IReadableDatabase $dbr, string $startMessage, array $where ) {
90 $this->output( $startMessage . "...\n" );
91 $dryRun = $this->hasOption( 'dry-run' );
92
93 $iterator = new BatchRowIterator(
94 $dbr,
96 ->from( 'user_properties' )
97 ->select( $dryRun ?
98 [ 'up_user', 'up_property', 'up_value' ] :
99 [ 'up_user', 'up_property' ] )
100 ->where( $where )
101 ->caller( __METHOD__ ),
102 [ 'up_user', 'up_property' ],
103 $this->getBatchSize()
104 );
105
106 $dbw = $this->getPrimaryDB();
107 $total = 0;
108 foreach ( $iterator as $batch ) {
109 $numRows = count( $batch );
110 $total += $numRows;
111 // Progress or something
112 $this->output( "..doing $numRows entries\n" );
113
114 // Delete our batch, then wait
115 $deleteWhere = [];
116 foreach ( $batch as $row ) {
117 if ( $dryRun ) {
118 $this->output(
119 " DRY RUN, would drop: " .
120 "[up_user] => '{$row->up_user}' " .
121 "[up_property] => '{$row->up_property}' " .
122 "[up_value] => '{$row->up_value}'\n"
123 );
124 continue;
125 }
126 $deleteWhere[$row->up_user][$row->up_property] = true;
127 }
128 if ( $deleteWhere && !$dryRun ) {
129 $dbw->newDeleteQueryBuilder()
130 ->deleteFrom( 'user_properties' )
131 ->where( $dbw->makeWhereFrom2d( $deleteWhere, 'up_user', 'up_property' ) )
132 ->caller( __METHOD__ )->execute();
133
134 $this->waitForReplication();
135 }
136 }
137 $this->output( "DONE! (handled $total entries)\n" );
138 }
139}
140
141// @codeCoverageIgnoreStart
142$maintClass = CleanupPreferences::class; // Tells it to run the class
143require_once RUN_MAINTENANCE_IF_MAIN;
144// @codeCoverageIgnoreEnd
Allows iterating a large number of rows in batches transparently.
Maintenance script that removes unused preferences from the database.
__construct()
Default constructor.
execute()
We will do this in three passes 1) The easiest is to drop the hidden preferences from the database.
A class containing constants representing the names of configuration variables.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
getBatchSize()
Returns batch size.
output( $out, $channel=null)
Throw some output to the user.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
waitForReplication()
Wait for replica DB servers to catch up.
hasOption( $name)
Checks to see if a particular option was set.
getReplicaDB(string|false $virtualDomain=false)
getHookRunner()
Get a HookRunner for running core hooks.
getServiceContainer()
Returns the main service container.
getPrimaryDB(string|false $virtualDomain=false)
addDescription( $text)
Set the description text.
Provides access to user options.
Content of like value.
Definition LikeValue.php:14
A database connection without write operations.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
expr(string $field, string $op, $value)
See Expression::__construct()
anyString()
Returns a token for buildLike() that denotes a '' to be used in a LIKE query.