MediaWiki REL1_34
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 ] );
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 " );
159 $found = 0;
160 foreach ( $result as $row ) {
161 $result2 = $dbw->query( "
162 SELECT MAX(rev_timestamp) as max_timestamp
163 FROM $revision
164 WHERE rev_page=" . (int)( $row->page_id )
165 );
166 $row2 = $dbw->fetchObject( $result2 );
167 if ( $row2 ) {
168 if ( $row->rev_timestamp != $row2->max_timestamp ) {
169 if ( $found == 0 ) {
170 $this->output( sprintf( "%10s %10s %14s %14s\n",
171 'page_id', 'rev_id', 'timestamp', 'max timestamp' ) );
172 }
173 ++$found;
174 $this->output( sprintf( "%10d %10d %14s %14s\n",
175 $row->page_id,
176 $row->page_latest,
177 $row->rev_timestamp,
178 $row2->max_timestamp ) );
179 if ( $fix ) {
180 # ...
181 $maxId = $dbw->selectField(
182 'revision',
183 'rev_id',
184 [
185 'rev_page' => $row->page_id,
186 'rev_timestamp' => $row2->max_timestamp ] );
187 $this->output( "... updating to revision $maxId\n" );
188 $maxRev = Revision::newFromId( $maxId );
189 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
190 $article = WikiPage::factory( $title );
191 $article->updateRevisionOn( $dbw, $maxRev );
192 }
193 }
194 } else {
195 $this->output( "wtf\n" );
196 }
197 }
198
199 if ( $found ) {
200 $this->output( "Found $found pages with incorrect latest revision.\n" );
201 } else {
202 $this->output( "No pages with incorrect latest revision. Yay!\n" );
203 }
204 if ( !$fix && $found > 0 ) {
205 $this->output( "Run again with --fix to remove these entries automatically.\n" );
206 }
207
208 if ( $fix ) {
209 $dbw->unlockTables( __METHOD__ );
210 }
211 }
212}
213
214$maintClass = Orphans::class;
215require_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 exists.
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
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition Revision.php:119
Advanced database interface for IDatabase handles that include maintenance methods.
$maintClass
Definition orphans.php:214
const DB_MASTER
Definition defines.php:26