24 require_once __DIR__ .
'/Maintenance.php';
43 parent::__construct();
45 This script refreshes the category membership counts stored in the category
46 table. As time passes, these counts often drift from the actual number of
47 category members. The script identifies rows where the value in the category
48 table does not match the number of categorylinks rows
for that category, and
49 updates the category table accordingly.
51 To fully refresh the data in the category table, you need to run
this script
52 for all three modes. Alternatively, just one mode can be run
if required.
57 '(REQUIRED) Which category count column to recompute: "pages", "subcats", "files" or "all".',
63 'Only recount categories with cat_id greater than the given value',
69 'Wait this many milliseconds after each batch. Default: 0',
76 'Skip running cleanupEmptyCategories if the "page" mode is selected',
85 $originalMode = $this->
getOption(
'mode' );
86 if ( !in_array( $originalMode, [
'pages',
'subcats',
'files',
'all' ] ) ) {
87 $this->
fatalError(
'Please specify a valid mode: one of "pages", "subcats", "files" or "all".' );
90 if ( $originalMode ===
'all' ) {
91 $modes = [
'pages',
'subcats',
'files' ];
93 $modes = [ $originalMode ];
96 foreach ( $modes as $mode ) {
97 $this->
output(
"Starting to recount {$mode} counts.\n" );
98 $this->minimumId = intval( $this->
getOption(
'begin', 0 ) );
102 while ( ( $result = $this->
doWork( $mode ) ) !==
false ) {
103 $affectedRows += $result;
104 usleep( $this->
getOption(
'throttle', 0 ) * 1000 );
107 $this->
output(
"Updated the {$mode} counts of $affectedRows categories.\n" );
111 $this->
output(
"Done!\n" );
112 if ( $originalMode !==
'all' ) {
113 $this->
output(
"Now run the script using the other --mode options if you haven't already.\n" );
116 if ( in_array(
'pages', $modes ) ) {
117 if ( $this->
hasOption(
'skip-cleanup' ) ) {
119 "Also run 'php cleanupEmptyCategories.php --mode remove' to remove empty,\n" .
120 "nonexistent categories from the category table.\n\n" );
122 $this->
output(
"Running cleanupEmptyCategories.php\n" );
123 $cleanup = $this->
runChild( CleanupEmptyCategories::class );
124 '@phan-var CleanupEmptyCategories $cleanup';
127 $cleanup->loadParamsAndArgs( $this->mSelf, [], [] );
129 $cleanup->setForce(
true );
136 $this->
output(
"Finding up to {$this->getBatchSize()} drifted rows " .
137 "greater than cat_id {$this->minimumId}...\n" );
139 $countingConds = [
'cl_to = cat_title' ];
140 if ( $mode ===
'subcats' ) {
141 $countingConds[
'cl_type'] =
'subcat';
142 } elseif ( $mode ===
'files' ) {
143 $countingConds[
'cl_type'] =
'file';
147 $countingSubquery =
$dbr->selectSQLText(
'categorylinks',
156 $idsToUpdate =
$dbr->selectFieldValues(
'category',
159 'cat_id > ' . (
int)$this->minimumId,
160 "cat_{$mode} != ($countingSubquery)"
165 if ( !$idsToUpdate ) {
168 $this->
output(
"Updating cat_{$mode} field on " .
169 count( $idsToUpdate ) .
" rows...\n" );
175 $this->minimumId = end( $idsToUpdate );
179 $res = $dbw->select(
'category',
180 [
'cat_id',
'count' =>
"($countingSubquery)" ],
181 [
'cat_id' => $idsToUpdate ],
190 foreach (
$res as $row ) {
191 $dbw->update(
'category',
192 [
"cat_{$mode}" => $row->count ],
194 'cat_id' => $row->cat_id,
195 "cat_{$mode} != " . (
int)( $row->count ),
198 $affectedRows += $dbw->affectedRows();
201 MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
203 return $affectedRows;
208 require_once RUN_MAINTENANCE_IF_MAIN;
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
runChild( $maintClass, $classFile=null)
Run a child maintenance script.
getBatchSize()
Returns batch size.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
Maintenance script that refreshes category membership counts in the category table.
__construct()
Default constructor.
execute()
Do the actual work.