MediaWiki REL1_35
orphans.php
Go to the documentation of this file.
1<?php
31require_once __DIR__ . '/Maintenance.php';
32
35
42class Orphans extends Maintenance {
43 public function __construct() {
44 parent::__construct();
45 $this->addDescription( "Look for 'orphan' revisions hooked to pages which don't exist\n" .
46 "and 'childless' pages with no revisions\n" .
47 "Then, kill the poor widows and orphans\n" .
48 "Man this is depressing"
49 );
50 $this->addOption( 'fix', 'Actually fix broken entries' );
51 }
52
53 public function execute() {
54 $this->checkOrphans( $this->hasOption( 'fix' ) );
55 $this->checkSeparation( $this->hasOption( 'fix' ) );
56 }
57
63 private function lockTables( $db, $extraTable = [] ) {
64 $tbls = [ 'page', 'revision', 'redirect' ];
65 if ( $extraTable ) {
66 $tbls = array_merge( $tbls, $extraTable );
67 }
68 $db->lockTables( [], $tbls, __METHOD__ );
69 }
70
75 private function checkOrphans( $fix ) {
76 $dbw = $this->getDB( DB_MASTER );
77 $commentStore = CommentStore::getStore();
78
79 if ( $fix ) {
80 $this->lockTables( $dbw );
81 }
82
83 $commentQuery = $commentStore->getJoin( 'rev_comment' );
84 $actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
85
86 $this->output( "Checking for orphan revision table entries... "
87 . "(this may take a while on a large wiki)\n" );
88 $result = $dbw->select(
89 [ 'revision', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
90 [ 'rev_id', 'rev_page', 'rev_timestamp' ] + $commentQuery['fields'] + $actorQuery['fields'],
91 [ 'page_id' => null ],
92 __METHOD__,
93 [],
94 [ 'page' => [ 'LEFT JOIN', [ 'rev_page=page_id' ] ] ] + $commentQuery['joins']
95 + $actorQuery['joins']
96 );
97 $orphans = $result->numRows();
98 if ( $orphans > 0 ) {
99 $this->output( "$orphans orphan revisions...\n" );
100 $this->output( sprintf(
101 "%10s %10s %14s %20s %s\n",
102 'rev_id', 'rev_page', 'rev_timestamp', 'rev_user_text', 'rev_comment'
103 ) );
104
105 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
106 foreach ( $result as $row ) {
107 $comment = $commentStore->getComment( 'rev_comment', $row )->text;
108 if ( $comment !== '' ) {
109 $comment = '(' . $contLang->truncateForVisual( $comment, 40 ) . ')';
110 }
111 $rev_user_text = $contLang->truncateForVisual( $row->rev_user_text, 20 );
112 # pad $rev_user_text to 20 characters. Note that this may
113 # yield poor results if $rev_user_text contains combining
114 # or half-width characters. Alas.
115 if ( mb_strlen( $rev_user_text ) < 20 ) {
116 $rev_user_text = str_repeat( ' ', 20 - mb_strlen( $rev_user_text ) );
117 }
118 $this->output( sprintf( "%10d %10d %14s %s %s\n",
119 $row->rev_id,
120 $row->rev_page,
121 $row->rev_timestamp,
122 $rev_user_text,
123 $comment ) );
124 if ( $fix ) {
125 $dbw->delete( 'revision', [ 'rev_id' => $row->rev_id ], __METHOD__ );
126 }
127 }
128 if ( !$fix ) {
129 $this->output( "Run again with --fix to remove these entries automatically.\n" );
130 }
131 } else {
132 $this->output( "No orphans! Yay!\n" );
133 }
134
135 if ( $fix ) {
136 $dbw->unlockTables( __METHOD__ );
137 }
138 }
139
144 private function checkSeparation( $fix ) {
145 $dbw = $this->getDB( DB_MASTER );
146 $page = $dbw->tableName( 'page' );
147 $revision = $dbw->tableName( 'revision' );
148
149 if ( $fix ) {
150 $this->lockTables( $dbw, [ 'user', 'text' ] );
151 }
152
153 $this->output( "\nChecking for pages whose page_latest links are incorrect... "
154 . "(this may take a while on a large wiki)\n" );
155 $result = $dbw->query( "
156 SELECT *
157 FROM $page LEFT JOIN $revision ON page_latest=rev_id
158 ", __METHOD__ );
159 $found = 0;
160 $revLookup = MediaWikiServices::getInstance()->getRevisionLookup();
161 foreach ( $result as $row ) {
162 $result2 = $dbw->query( "
163 SELECT MAX(rev_timestamp) as max_timestamp
164 FROM $revision
165 WHERE rev_page=" . (int)( $row->page_id ),
166 __METHOD__
167 );
168 $row2 = $dbw->fetchObject( $result2 );
169 if ( $row2 ) {
170 if ( $row->rev_timestamp != $row2->max_timestamp ) {
171 if ( $found == 0 ) {
172 $this->output( sprintf( "%10s %10s %14s %14s\n",
173 'page_id', 'rev_id', 'timestamp', 'max timestamp' ) );
174 }
175 ++$found;
176 $this->output( sprintf( "%10d %10d %14s %14s\n",
177 $row->page_id,
178 $row->page_latest,
179 $row->rev_timestamp,
180 $row2->max_timestamp ) );
181 if ( $fix ) {
182 # ...
183 $maxId = $dbw->selectField(
184 'revision',
185 'rev_id',
186 [
187 'rev_page' => $row->page_id,
188 'rev_timestamp' => $row2->max_timestamp
189 ],
190 __METHOD__
191 );
192 $this->output( "... updating to revision $maxId\n" );
193 $maxRev = $revLookup->getRevisionById( $maxId );
194 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
195 $article = WikiPage::factory( $title );
196 $article->updateRevisionOn( $dbw, $maxRev );
197 }
198 }
199 } else {
200 $this->output( "wtf\n" );
201 }
202 }
203
204 if ( $found ) {
205 $this->output( "Found $found pages with incorrect latest revision.\n" );
206 } else {
207 $this->output( "No pages with incorrect latest revision. Yay!\n" );
208 }
209 if ( !$fix && $found > 0 ) {
210 $this->output( "Run again with --fix to remove these entries automatically.\n" );
211 }
212
213 if ( $fix ) {
214 $dbw->unlockTables( __METHOD__ );
215 }
216 }
217}
218
219$maintClass = Orphans::class;
220require_once RUN_MAINTENANCE_IF_MAIN;
getDB()
const RUN_MAINTENANCE_IF_MAIN
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Maintenance script that looks for 'orphan' revisions hooked to pages which don't exist and 'childless...
Definition orphans.php:42
lockTables( $db, $extraTable=[])
Lock the appropriate tables for the script.
Definition orphans.php:63
checkSeparation( $fix)
Check for pages where page_latest is wrong.
Definition orphans.php:144
execute()
Do the actual work.
Definition orphans.php:53
__construct()
Default constructor.
Definition orphans.php:43
checkOrphans( $fix)
Check for orphan revisions.
Definition orphans.php:75
Advanced database interface for IDatabase handles that include maintenance methods.
$maintClass
Definition orphans.php:219
const DB_MASTER
Definition defines.php:29