MediaWiki REL1_31
orphans.php
Go to the documentation of this file.
1<?php
31require_once __DIR__ . '/Maintenance.php';
32
34
41class Orphans extends Maintenance {
42 public function __construct() {
43 parent::__construct();
44 $this->addDescription( "Look for 'orphan' revisions hooked to pages which don't exist\n" .
45 "and 'childless' pages with no revisions\n" .
46 "Then, kill the poor widows and orphans\n" .
47 "Man this is depressing"
48 );
49 $this->addOption( 'fix', 'Actually fix broken entries' );
50 }
51
52 public function execute() {
53 $this->checkOrphans( $this->hasOption( 'fix' ) );
54 $this->checkSeparation( $this->hasOption( 'fix' ) );
55 # Does not work yet, do not use
56 # $this->checkWidows( $this->hasOption( 'fix' ) );
57 }
58
64 private function lockTables( $db, $extraTable = [] ) {
65 $tbls = [ 'page', 'revision', 'redirect' ];
66 if ( $extraTable ) {
67 $tbls = array_merge( $tbls, $extraTable );
68 }
69 $db->lockTables( [], $tbls, __METHOD__, false );
70 }
71
76 private function checkOrphans( $fix ) {
77 $dbw = $this->getDB( DB_MASTER );
78 $commentStore = CommentStore::getStore();
79
80 if ( $fix ) {
81 $this->lockTables( $dbw );
82 }
83
84 $commentQuery = $commentStore->getJoin( 'rev_comment' );
85 $actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
86
87 $this->output( "Checking for orphan revision table entries... "
88 . "(this may take a while on a large wiki)\n" );
89 $result = $dbw->select(
90 [ 'revision', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
91 [ 'rev_id', 'rev_page', 'rev_timestamp' ] + $commentQuery['fields'] + $actorQuery['fields'],
92 [ 'page_id' => null ],
93 __METHOD__,
94 [],
95 [ 'page' => [ 'LEFT JOIN', [ 'rev_page=page_id' ] ] ] + $commentQuery['joins']
96 + $actorQuery['joins']
97 );
98 $orphans = $result->numRows();
99 if ( $orphans > 0 ) {
100 global $wgContLang;
101
102 $this->output( "$orphans orphan revisions...\n" );
103 $this->output( sprintf(
104 "%10s %10s %14s %20s %s\n",
105 'rev_id', 'rev_page', 'rev_timestamp', 'rev_user_text', 'rev_comment'
106 ) );
107
108 foreach ( $result as $row ) {
109 $comment = $commentStore->getComment( 'rev_comment', $row )->text;
110 if ( $comment !== '' ) {
111 $comment = '(' . $wgContLang->truncate( $comment, 40 ) . ')';
112 }
113 $this->output( sprintf( "%10d %10d %14s %20s %s\n",
114 $row->rev_id,
115 $row->rev_page,
116 $row->rev_timestamp,
117 $wgContLang->truncate( $row->rev_user_text, 17 ),
118 $comment ) );
119 if ( $fix ) {
120 $dbw->delete( 'revision', [ 'rev_id' => $row->rev_id ] );
121 }
122 }
123 if ( !$fix ) {
124 $this->output( "Run again with --fix to remove these entries automatically.\n" );
125 }
126 } else {
127 $this->output( "No orphans! Yay!\n" );
128 }
129
130 if ( $fix ) {
131 $dbw->unlockTables( __METHOD__ );
132 }
133 }
134
141 private function checkWidows( $fix ) {
142 $dbw = $this->getDB( DB_MASTER );
143 $page = $dbw->tableName( 'page' );
144 $revision = $dbw->tableName( 'revision' );
145
146 if ( $fix ) {
147 $this->lockTables( $dbw );
148 }
149
150 $this->output( "\nChecking for childless page table entries... "
151 . "(this may take a while on a large wiki)\n" );
152 $result = $dbw->query( "
153 SELECT *
154 FROM $page LEFT OUTER JOIN $revision ON page_latest=rev_id
155 WHERE rev_id IS NULL
156 " );
157 $widows = $result->numRows();
158 if ( $widows > 0 ) {
159 $this->output( "$widows childless pages...\n" );
160 $this->output( sprintf( "%10s %11s %2s %s\n", 'page_id', 'page_latest', 'ns', 'page_title' ) );
161 foreach ( $result as $row ) {
162 printf( "%10d %11d %2d %s\n",
163 $row->page_id,
164 $row->page_latest,
165 $row->page_namespace,
166 $row->page_title );
167 if ( $fix ) {
168 $dbw->delete( 'page', [ 'page_id' => $row->page_id ] );
169 }
170 }
171 if ( !$fix ) {
172 $this->output( "Run again with --fix to remove these entries automatically.\n" );
173 }
174 } else {
175 $this->output( "No childless pages! Yay!\n" );
176 }
177
178 if ( $fix ) {
179 $dbw->unlockTables( __METHOD__ );
180 }
181 }
182
187 private function checkSeparation( $fix ) {
188 $dbw = $this->getDB( DB_MASTER );
189 $page = $dbw->tableName( 'page' );
190 $revision = $dbw->tableName( 'revision' );
191
192 if ( $fix ) {
193 $this->lockTables( $dbw, [ 'user', 'text' ] );
194 }
195
196 $this->output( "\nChecking for pages whose page_latest links are incorrect... "
197 . "(this may take a while on a large wiki)\n" );
198 $result = $dbw->query( "
199 SELECT *
200 FROM $page LEFT OUTER JOIN $revision ON page_latest=rev_id
201 " );
202 $found = 0;
203 foreach ( $result as $row ) {
204 $result2 = $dbw->query( "
205 SELECT MAX(rev_timestamp) as max_timestamp
206 FROM $revision
207 WHERE rev_page=" . (int)( $row->page_id )
208 );
209 $row2 = $dbw->fetchObject( $result2 );
210 if ( $row2 ) {
211 if ( $row->rev_timestamp != $row2->max_timestamp ) {
212 if ( $found == 0 ) {
213 $this->output( sprintf( "%10s %10s %14s %14s\n",
214 'page_id', 'rev_id', 'timestamp', 'max timestamp' ) );
215 }
216 ++$found;
217 $this->output( sprintf( "%10d %10d %14s %14s\n",
218 $row->page_id,
219 $row->page_latest,
220 $row->rev_timestamp,
221 $row2->max_timestamp ) );
222 if ( $fix ) {
223 # ...
224 $maxId = $dbw->selectField(
225 'revision',
226 'rev_id',
227 [
228 'rev_page' => $row->page_id,
229 'rev_timestamp' => $row2->max_timestamp ] );
230 $this->output( "... updating to revision $maxId\n" );
231 $maxRev = Revision::newFromId( $maxId );
232 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
233 $article = WikiPage::factory( $title );
234 $article->updateRevisionOn( $dbw, $maxRev );
235 }
236 }
237 } else {
238 $this->output( "wtf\n" );
239 }
240 }
241
242 if ( $found ) {
243 $this->output( "Found $found pages with incorrect latest revision.\n" );
244 } else {
245 $this->output( "No pages with incorrect latest revision. Yay!\n" );
246 }
247 if ( !$fix && $found > 0 ) {
248 $this->output( "Run again with --fix to remove these entries automatically.\n" );
249 }
250
251 if ( $fix ) {
252 $dbw->unlockTables( __METHOD__ );
253 }
254 }
255}
256
257$maintClass = Orphans::class;
258require_once RUN_MAINTENANCE_IF_MAIN;
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
getDB( $db, $groups=[], $wiki=false)
Returns a database to be used by current maintenance script.
hasOption( $name)
Checks to see if a particular param exists.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
Maintenance script that looks for 'orphan' revisions hooked to pages which don't exist and 'childless...
Definition orphans.php:41
lockTables( $db, $extraTable=[])
Lock the appropriate tables for the script.
Definition orphans.php:64
checkSeparation( $fix)
Check for pages where page_latest is wrong.
Definition orphans.php:187
checkWidows( $fix)
Definition orphans.php:141
execute()
Do the actual work.
Definition orphans.php:52
__construct()
Default constructor.
Definition orphans.php:42
checkOrphans( $fix)
Check for orphan revisions.
Definition orphans.php:76
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
Definition design.txt:57
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling output() to send it all. It could be easily changed to send incrementally if that becomes useful
Advanced database interface for IDatabase handles that include maintenance methods.
require_once RUN_MAINTENANCE_IF_MAIN
$maintClass
Definition orphans.php:257
const DB_MASTER
Definition defines.php:29