MediaWiki master
cleanupPreferences.php
Go to the documentation of this file.
1<?php
13// @codeCoverageIgnoreStart
14require_once __DIR__ . '/Maintenance.php';
15// @codeCoverageIgnoreEnd
16
24
31 public function __construct() {
32 parent::__construct();
33 $this->addDescription( 'Clean up hidden preferences or removed preferences' );
34 $this->setBatchSize( 50 );
35 $this->addOption( 'dry-run', 'Print debug info instead of actually deleting' );
36 $this->addOption( 'hidden', 'Drop hidden preferences ($wgHiddenPrefs)' );
37 $this->addOption( 'unknown',
38 'Drop unknown preferences (not in $wgDefaultUserOptions or prefixed with "userjs-")' );
39 }
40
49 public function execute() {
50 $dbr = $this->getReplicaDB();
51 $hidden = $this->hasOption( 'hidden' );
52 $unknown = $this->hasOption( 'unknown' );
53
54 if ( !$hidden && !$unknown ) {
55 $this->output( "Did not select one of --hidden, --unknown, exiting\n" );
56 return;
57 }
58
59 // Remove hidden prefs. Iterate over them to avoid the IN on a large table
60 if ( $hidden ) {
61 $hiddenPrefs = $this->getConfig()->get( MainConfigNames::HiddenPrefs );
62 if ( !$hiddenPrefs ) {
63 $this->output( "No hidden preferences, skipping\n" );
64 }
65 foreach ( $hiddenPrefs as $hiddenPref ) {
66 $this->deleteByWhere(
67 $dbr,
68 'Dropping hidden preferences',
69 [ 'up_property' => $hiddenPref ]
70 );
71 }
72 }
73
74 // Remove unknown preferences. Special-case 'userjs-' as we can't control those names.
75 if ( $unknown ) {
76 $defaultUserOptions = $this->getServiceContainer()->getUserOptionsLookup()->getDefaultOptions( null );
77 $where = [
78 $dbr->expr( 'up_property', IExpression::NOT_LIKE,
79 new LikeValue( 'userjs-', $dbr->anyString() ) ),
80 $dbr->expr( 'up_property', IExpression::NOT_LIKE,
81 new LikeValue( UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX, $dbr->anyString() ) ),
82 $dbr->expr( 'up_property', '!=', array_keys( $defaultUserOptions ) ),
83 ];
84 // Allow extensions to add to the where clause to prevent deletion of their own prefs.
85 $this->getHookRunner()->onDeleteUnknownPreferences( $where, $dbr );
86 $this->deleteByWhere( $dbr, 'Dropping unknown preferences', $where );
87 }
88 }
89
90 private function deleteByWhere( IReadableDatabase $dbr, string $startMessage, array $where ) {
91 $this->output( $startMessage . "...\n" );
92 $dryRun = $this->hasOption( 'dry-run' );
93
94 $iterator = new BatchRowIterator(
95 $dbr,
97 ->from( 'user_properties' )
98 ->select( $dryRun ?
99 [ 'up_user', 'up_property', 'up_value' ] :
100 [ 'up_user', 'up_property' ] )
101 ->where( $where )
102 ->caller( __METHOD__ ),
103 [ 'up_user', 'up_property' ],
104 $this->getBatchSize()
105 );
106
107 $dbw = $this->getPrimaryDB();
108 $total = 0;
109 foreach ( $iterator as $batch ) {
110 $numRows = count( $batch );
111 $total += $numRows;
112 // Progress or something
113 $this->output( "..doing $numRows entries\n" );
114
115 // Delete our batch, then wait
116 $deleteWhere = [];
117 foreach ( $batch as $row ) {
118 if ( $dryRun ) {
119 $this->output(
120 " DRY RUN, would drop: " .
121 "[up_user] => '{$row->up_user}' " .
122 "[up_property] => '{$row->up_property}' " .
123 "[up_value] => '{$row->up_value}'\n"
124 );
125 continue;
126 }
127 $deleteWhere[$row->up_user][$row->up_property] = true;
128 }
129 if ( $deleteWhere && !$dryRun ) {
130 $dbw->newDeleteQueryBuilder()
131 ->deleteFrom( 'user_properties' )
132 ->where( $dbw->makeWhereFrom2d( $deleteWhere, 'up_user', 'up_property' ) )
133 ->caller( __METHOD__ )->execute();
134
135 $this->waitForReplication();
136 }
137 }
138 $this->output( "DONE! (handled $total entries)\n" );
139 }
140}
141
142// @codeCoverageIgnoreStart
143$maintClass = CleanupPreferences::class; // Tells it to run the class
144require_once RUN_MAINTENANCE_IF_MAIN;
145// @codeCoverageIgnoreEnd
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.
Allows iterating a large number of rows in batches transparently.
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.