MediaWiki REL1_37
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_PRIMARY );
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_PRIMARY );
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 $services = MediaWikiServices::getInstance();
161 $revLookup = $services->getRevisionLookup();
162 $wikiPageFactory = $services->getWikiPageFactory();
163 foreach ( $result as $row ) {
164 $result2 = $dbw->query( "
165 SELECT MAX(rev_timestamp) as max_timestamp
166 FROM $revision
167 WHERE rev_page=" . (int)( $row->page_id ),
168 __METHOD__
169 );
170 $row2 = $dbw->fetchObject( $result2 );
171 if ( $row2 ) {
172 if ( $row->rev_timestamp != $row2->max_timestamp ) {
173 if ( $found == 0 ) {
174 $this->output( sprintf( "%10s %10s %14s %14s\n",
175 'page_id', 'rev_id', 'timestamp', 'max timestamp' ) );
176 }
177 ++$found;
178 $this->output( sprintf( "%10d %10d %14s %14s\n",
179 $row->page_id,
180 $row->page_latest,
181 $row->rev_timestamp,
182 $row2->max_timestamp ) );
183 if ( $fix ) {
184 # ...
185 $maxId = $dbw->selectField(
186 'revision',
187 'rev_id',
188 [
189 'rev_page' => $row->page_id,
190 'rev_timestamp' => $row2->max_timestamp
191 ],
192 __METHOD__
193 );
194 $this->output( "... updating to revision $maxId\n" );
195 $maxRev = $revLookup->getRevisionById( $maxId );
196 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
197 $article = $wikiPageFactory->newFromTitle( $title );
198 $article->updateRevisionOn( $dbw, $maxRev );
199 }
200 }
201 } else {
202 $this->output( "wtf\n" );
203 }
204 }
205
206 if ( $found ) {
207 $this->output( "Found $found pages with incorrect latest revision.\n" );
208 } else {
209 $this->output( "No pages with incorrect latest revision. Yay!\n" );
210 }
211 if ( !$fix && $found > 0 ) {
212 $this->output( "Run again with --fix to remove these entries automatically.\n" );
213 }
214
215 if ( $fix ) {
216 $dbw->unlockTables( __METHOD__ );
217 }
218 }
219}
220
221$maintClass = Orphans::class;
222require_once RUN_MAINTENANCE_IF_MAIN;
getDB()
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:221
const DB_PRIMARY
Definition defines.php:27