MediaWiki  1.23.12
Maintenance.php
Go to the documentation of this file.
1 <?php
23 // Make sure we're on PHP5.3.2 or better
24 if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
25  // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
26  require_once dirname( __FILE__ ) . '/../includes/PHPVersionError.php';
27  wfPHPVersionError( 'cli' );
28 }
29 
35 // Define this so scripts can easily find doMaintenance.php
36 define( 'RUN_MAINTENANCE_IF_MAIN', __DIR__ . '/doMaintenance.php' );
37 define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
38 
39 $maintClass = false;
40 
51 abstract class Maintenance {
52 
57  const DB_NONE = 0;
58  const DB_STD = 1;
59  const DB_ADMIN = 2;
60 
61  // Const for getStdin()
62  const STDIN_ALL = 'all';
63 
64  // This is the desired params
65  protected $mParams = array();
66 
67  // Array of mapping short parameters to long ones
68  protected $mShortParamsMap = array();
69 
70  // Array of desired args
71  protected $mArgList = array();
72 
73  // This is the list of options that were actually passed
74  protected $mOptions = array();
75 
76  // This is the list of arguments that were actually passed
77  protected $mArgs = array();
78 
79  // Name of the script currently running
80  protected $mSelf;
81 
82  // Special vars for params that are always used
83  protected $mQuiet = false;
84  protected $mDbUser, $mDbPass;
85 
86  // A description of the script, children should change this
87  protected $mDescription = '';
88 
89  // Have we already loaded our user input?
90  protected $mInputLoaded = false;
91 
98  protected $mBatchSize = null;
99 
100  // Generic options added by addDefaultParams()
101  private $mGenericParameters = array();
102  // Generic options which might or not be supported by the script
103  private $mDependantParameters = array();
104 
109  private $mDb = null;
110 
115  public $fileHandle;
116 
121  public function __construct() {
122  // Setup $IP, using MW_INSTALL_PATH if it exists
123  global $IP;
124  $IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
125  ? getenv( 'MW_INSTALL_PATH' )
126  : realpath( __DIR__ . '/..' );
127 
128  $this->addDefaultParams();
129  register_shutdown_function( array( $this, 'outputChanneled' ), false );
130  }
131 
139  public static function shouldExecute() {
140  $bt = debug_backtrace();
141  $count = count( $bt );
142  if ( $count < 2 ) {
143  return false; // sanity
144  }
145  if ( $bt[0]['class'] !== 'Maintenance' || $bt[0]['function'] !== 'shouldExecute' ) {
146  return false; // last call should be to this function
147  }
148  $includeFuncs = array( 'require_once', 'require', 'include', 'include_once' );
149  for ( $i = 1; $i < $count; $i++ ) {
150  if ( !in_array( $bt[$i]['function'], $includeFuncs ) ) {
151  return false; // previous calls should all be "requires"
152  }
153  }
154  return true;
155  }
156 
160  abstract public function execute();
161 
172  protected function addOption( $name, $description, $required = false, $withArg = false, $shortName = false ) {
173  $this->mParams[$name] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg, 'shortName' => $shortName );
174  if ( $shortName !== false ) {
175  $this->mShortParamsMap[$shortName] = $name;
176  }
177  }
178 
184  protected function hasOption( $name ) {
185  return isset( $this->mOptions[$name] );
186  }
187 
194  protected function getOption( $name, $default = null ) {
195  if ( $this->hasOption( $name ) ) {
196  return $this->mOptions[$name];
197  } else {
198  // Set it so we don't have to provide the default again
199  $this->mOptions[$name] = $default;
200  return $this->mOptions[$name];
201  }
202  }
203 
210  protected function addArg( $arg, $description, $required = true ) {
211  $this->mArgList[] = array(
212  'name' => $arg,
213  'desc' => $description,
214  'require' => $required
215  );
216  }
217 
222  protected function deleteOption( $name ) {
223  unset( $this->mParams[$name] );
224  }
225 
230  protected function addDescription( $text ) {
231  $this->mDescription = $text;
232  }
233 
239  protected function hasArg( $argId = 0 ) {
240  return isset( $this->mArgs[$argId] );
241  }
242 
249  protected function getArg( $argId = 0, $default = null ) {
250  return $this->hasArg( $argId ) ? $this->mArgs[$argId] : $default;
251  }
252 
257  protected function setBatchSize( $s = 0 ) {
258  $this->mBatchSize = $s;
259 
260  // If we support $mBatchSize, show the option.
261  // Used to be in addDefaultParams, but in order for that to
262  // work, subclasses would have to call this function in the constructor
263  // before they called parent::__construct which is just weird
264  // (and really wasn't done).
265  if ( $this->mBatchSize ) {
266  $this->addOption( 'batch-size', 'Run this many operations ' .
267  'per batch, default: ' . $this->mBatchSize, false, true );
268  if ( isset( $this->mParams['batch-size'] ) ) {
269  // This seems a little ugly...
270  $this->mDependantParameters['batch-size'] = $this->mParams['batch-size'];
271  }
272  }
273  }
274 
279  public function getName() {
280  return $this->mSelf;
281  }
282 
290  protected function getStdin( $len = null ) {
291  if ( $len == Maintenance::STDIN_ALL ) {
292  return file_get_contents( 'php://stdin' );
293  }
294  $f = fopen( 'php://stdin', 'rt' );
295  if ( !$len ) {
296  return $f;
297  }
298  $input = fgets( $f, $len );
299  fclose( $f );
300  return rtrim( $input );
301  }
302 
306  public function isQuiet() {
307  return $this->mQuiet;
308  }
309 
317  protected function output( $out, $channel = null ) {
318  if ( $this->mQuiet ) {
319  return;
320  }
321  if ( $channel === null ) {
322  $this->cleanupChanneled();
323  print $out;
324  } else {
325  $out = preg_replace( '/\n\z/', '', $out );
326  $this->outputChanneled( $out, $channel );
327  }
328  }
329 
336  protected function error( $err, $die = 0 ) {
337  $this->outputChanneled( false );
338  if ( PHP_SAPI == 'cli' ) {
339  fwrite( STDERR, $err . "\n" );
340  } else {
341  print $err;
342  }
343  $die = intval( $die );
344  if ( $die > 0 ) {
345  die( $die );
346  }
347  }
348 
349  private $atLineStart = true;
350  private $lastChannel = null;
351 
355  public function cleanupChanneled() {
356  if ( !$this->atLineStart ) {
357  print "\n";
358  $this->atLineStart = true;
359  }
360  }
361 
370  public function outputChanneled( $msg, $channel = null ) {
371  if ( $msg === false ) {
372  $this->cleanupChanneled();
373  return;
374  }
375 
376  // End the current line if necessary
377  if ( !$this->atLineStart && $channel !== $this->lastChannel ) {
378  print "\n";
379  }
380 
381  print $msg;
382 
383  $this->atLineStart = false;
384  if ( $channel === null ) {
385  // For unchanneled messages, output trailing newline immediately
386  print "\n";
387  $this->atLineStart = true;
388  }
389  $this->lastChannel = $channel;
390  }
391 
402  public function getDbType() {
403  return Maintenance::DB_STD;
404  }
405 
409  protected function addDefaultParams() {
410 
411  # Generic (non script dependant) options:
412 
413  $this->addOption( 'help', 'Display this help message', false, false, 'h' );
414  $this->addOption( 'quiet', 'Whether to supress non-error output', false, false, 'q' );
415  $this->addOption( 'conf', 'Location of LocalSettings.php, if not default', false, true );
416  $this->addOption( 'wiki', 'For specifying the wiki ID', false, true );
417  $this->addOption( 'globals', 'Output globals at the end of processing for debugging' );
418  $this->addOption( 'memory-limit', 'Set a specific memory limit for the script, "max" for no limit or "default" to avoid changing it' );
419  $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " .
420  "http://en.wikipedia.org. This is sometimes necessary because " .
421  "server name detection may fail in command line scripts.", false, true );
422  $this->addOption( 'profiler', 'Set to "text" or "trace" to show profiling output', false, true );
423 
424  # Save generic options to display them separately in help
425  $this->mGenericParameters = $this->mParams;
426 
427  # Script dependant options:
428 
429  // If we support a DB, show the options
430  if ( $this->getDbType() > 0 ) {
431  $this->addOption( 'dbuser', 'The DB user to use for this script', false, true );
432  $this->addOption( 'dbpass', 'The password to use for this script', false, true );
433  }
434 
435  # Save additional script dependant options to display
436  #┬áthem separately in help
437  $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters );
438  }
439 
447  public function runChild( $maintClass, $classFile = null ) {
448  // Make sure the class is loaded first
449  if ( !class_exists( $maintClass ) ) {
450  if ( $classFile ) {
451  require_once $classFile;
452  }
453  if ( !class_exists( $maintClass ) ) {
454  $this->error( "Cannot spawn child: $maintClass" );
455  }
456  }
457 
461  $child = new $maintClass();
462  $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
463  if ( !is_null( $this->mDb ) ) {
464  $child->setDB( $this->mDb );
465  }
466  return $child;
467  }
468 
472  public function setup() {
474 
475  # Abort if called from a web server
476  if ( isset( $_SERVER ) && isset( $_SERVER['REQUEST_METHOD'] ) ) {
477  $this->error( 'This script must be run from the command line', true );
478  }
479 
480  if ( $IP === null ) {
481  $this->error( "\$IP not set, aborting!\n" .
482  '(Did you forget to call parent::__construct() in your maintenance script?)', 1 );
483  }
484 
485  # Make sure we can handle script parameters
486  if ( !defined( 'HPHP_VERSION' ) && !ini_get( 'register_argc_argv' ) ) {
487  $this->error( 'Cannot get command line arguments, register_argc_argv is set to false', true );
488  }
489 
490  // Send PHP warnings and errors to stderr instead of stdout.
491  // This aids in diagnosing problems, while keeping messages
492  // out of redirected output.
493  if ( ini_get( 'display_errors' ) ) {
494  ini_set( 'display_errors', 'stderr' );
495  }
496 
497  $this->loadParamsAndArgs();
498  $this->maybeHelp();
499 
500  # Set the memory limit
501  # Note we need to set it again later in cache LocalSettings changed it
502  $this->adjustMemoryLimit();
503 
504  # Set max execution time to 0 (no limit). PHP.net says that
505  # "When running PHP from the command line the default setting is 0."
506  # But sometimes this doesn't seem to be the case.
507  ini_set( 'max_execution_time', 0 );
508 
509  $wgRequestTime = microtime( true );
510 
511  # Define us as being in MediaWiki
512  define( 'MEDIAWIKI', true );
513 
514  $wgCommandLineMode = true;
515 
516  # Turn off output buffering if it's on
517  while ( ob_get_level() > 0 ) {
518  ob_end_flush();
519  }
520 
521  $this->validateParamsAndArgs();
522  }
523 
533  public function memoryLimit() {
534  $limit = $this->getOption( 'memory-limit', 'max' );
535  $limit = trim( $limit, "\" '" ); // trim quotes in case someone misunderstood
536  return $limit;
537  }
538 
542  protected function adjustMemoryLimit() {
543  $limit = $this->memoryLimit();
544  if ( $limit == 'max' ) {
545  $limit = -1; // no memory limit
546  }
547  if ( $limit != 'default' ) {
548  ini_set( 'memory_limit', $limit );
549  }
550  }
551 
555  public function clearParamsAndArgs() {
556  $this->mOptions = array();
557  $this->mArgs = array();
558  $this->mInputLoaded = false;
559  }
560 
570  public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
571  # If we were given opts or args, set those and return early
572  if ( $self ) {
573  $this->mSelf = $self;
574  $this->mInputLoaded = true;
575  }
576  if ( $opts ) {
577  $this->mOptions = $opts;
578  $this->mInputLoaded = true;
579  }
580  if ( $args ) {
581  $this->mArgs = $args;
582  $this->mInputLoaded = true;
583  }
584 
585  # If we've already loaded input (either by user values or from $argv)
586  # skip on loading it again. The array_shift() will corrupt values if
587  # it's run again and again
588  if ( $this->mInputLoaded ) {
589  $this->loadSpecialVars();
590  return;
591  }
592 
593  global $argv;
594  $this->mSelf = array_shift( $argv );
595 
596  $options = array();
597  $args = array();
598 
599  # Parse arguments
600  for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
601  if ( $arg == '--' ) {
602  # End of options, remainder should be considered arguments
603  $arg = next( $argv );
604  while ( $arg !== false ) {
605  $args[] = $arg;
606  $arg = next( $argv );
607  }
608  break;
609  } elseif ( substr( $arg, 0, 2 ) == '--' ) {
610  # Long options
611  $option = substr( $arg, 2 );
612  if ( array_key_exists( $option, $options ) ) {
613  $this->error( "\nERROR: $option parameter given twice\n" );
614  $this->maybeHelp( true );
615  }
616  if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
617  $param = next( $argv );
618  if ( $param === false ) {
619  $this->error( "\nERROR: $option parameter needs a value after it\n" );
620  $this->maybeHelp( true );
621  }
622  $options[$option] = $param;
623  } else {
624  $bits = explode( '=', $option, 2 );
625  if ( count( $bits ) > 1 ) {
626  $option = $bits[0];
627  $param = $bits[1];
628  } else {
629  $param = 1;
630  }
631  $options[$option] = $param;
632  }
633  } elseif ( substr( $arg, 0, 1 ) == '-' ) {
634  # Short options
635  for ( $p = 1; $p < strlen( $arg ); $p++ ) {
636  $option = $arg { $p };
637  if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) {
638  $option = $this->mShortParamsMap[$option];
639  }
640  if ( array_key_exists( $option, $options ) ) {
641  $this->error( "\nERROR: $option parameter given twice\n" );
642  $this->maybeHelp( true );
643  }
644  if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
645  $param = next( $argv );
646  if ( $param === false ) {
647  $this->error( "\nERROR: $option parameter needs a value after it\n" );
648  $this->maybeHelp( true );
649  }
650  $options[$option] = $param;
651  } else {
652  $options[$option] = 1;
653  }
654  }
655  } else {
656  $args[] = $arg;
657  }
658  }
659 
660  $this->mOptions = $options;
661  $this->mArgs = $args;
662  $this->loadSpecialVars();
663  $this->mInputLoaded = true;
664  }
665 
669  protected function validateParamsAndArgs() {
670  $die = false;
671  # Check to make sure we've got all the required options
672  foreach ( $this->mParams as $opt => $info ) {
673  if ( $info['require'] && !$this->hasOption( $opt ) ) {
674  $this->error( "Param $opt required!" );
675  $die = true;
676  }
677  }
678  # Check arg list too
679  foreach ( $this->mArgList as $k => $info ) {
680  if ( $info['require'] && !$this->hasArg( $k ) ) {
681  $this->error( 'Argument <' . $info['name'] . '> required!' );
682  $die = true;
683  }
684  }
685 
686  if ( $die ) {
687  $this->maybeHelp( true );
688  }
689  }
690 
694  protected function loadSpecialVars() {
695  if ( $this->hasOption( 'dbuser' ) ) {
696  $this->mDbUser = $this->getOption( 'dbuser' );
697  }
698  if ( $this->hasOption( 'dbpass' ) ) {
699  $this->mDbPass = $this->getOption( 'dbpass' );
700  }
701  if ( $this->hasOption( 'quiet' ) ) {
702  $this->mQuiet = true;
703  }
704  if ( $this->hasOption( 'batch-size' ) ) {
705  $this->mBatchSize = intval( $this->getOption( 'batch-size' ) );
706  }
707  }
708 
713  protected function maybeHelp( $force = false ) {
714  if ( !$force && !$this->hasOption( 'help' ) ) {
715  return;
716  }
717 
718  $screenWidth = 80; // TODO: Caculate this!
719  $tab = " ";
720  $descWidth = $screenWidth - ( 2 * strlen( $tab ) );
721 
722  ksort( $this->mParams );
723  $this->mQuiet = false;
724 
725  // Description ...
726  if ( $this->mDescription ) {
727  $this->output( "\n" . $this->mDescription . "\n" );
728  }
729  $output = "\nUsage: php " . basename( $this->mSelf );
730 
731  // ... append parameters ...
732  if ( $this->mParams ) {
733  $output .= " [--" . implode( array_keys( $this->mParams ), "|--" ) . "]";
734  }
735 
736  // ... and append arguments.
737  if ( $this->mArgList ) {
738  $output .= ' ';
739  foreach ( $this->mArgList as $k => $arg ) {
740  if ( $arg['require'] ) {
741  $output .= '<' . $arg['name'] . '>';
742  } else {
743  $output .= '[' . $arg['name'] . ']';
744  }
745  if ( $k < count( $this->mArgList ) - 1 ) {
746  $output .= ' ';
747  }
748  }
749  }
750  $this->output( "$output\n\n" );
751 
752  # TODO abstract some repetitive code below
753 
754  // Generic parameters
755  $this->output( "Generic maintenance parameters:\n" );
756  foreach ( $this->mGenericParameters as $par => $info ) {
757  if ( $info['shortName'] !== false ) {
758  $par .= " (-{$info['shortName']})";
759  }
760  $this->output(
761  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
762  "\n$tab$tab" ) . "\n"
763  );
764  }
765  $this->output( "\n" );
766 
767  $scriptDependantParams = $this->mDependantParameters;
768  if ( count( $scriptDependantParams ) > 0 ) {
769  $this->output( "Script dependant parameters:\n" );
770  // Parameters description
771  foreach ( $scriptDependantParams as $par => $info ) {
772  if ( $info['shortName'] !== false ) {
773  $par .= " (-{$info['shortName']})";
774  }
775  $this->output(
776  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
777  "\n$tab$tab" ) . "\n"
778  );
779  }
780  $this->output( "\n" );
781  }
782 
783  // Script specific parameters not defined on construction by
784  // Maintenance::addDefaultParams()
785  $scriptSpecificParams = array_diff_key(
786  # all script parameters:
787  $this->mParams,
788  # remove the Maintenance default parameters:
789  $this->mGenericParameters,
790  $this->mDependantParameters
791  );
792  if ( count( $scriptSpecificParams ) > 0 ) {
793  $this->output( "Script specific parameters:\n" );
794  // Parameters description
795  foreach ( $scriptSpecificParams as $par => $info ) {
796  if ( $info['shortName'] !== false ) {
797  $par .= " (-{$info['shortName']})";
798  }
799  $this->output(
800  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
801  "\n$tab$tab" ) . "\n"
802  );
803  }
804  $this->output( "\n" );
805  }
806 
807  // Print arguments
808  if ( count( $this->mArgList ) > 0 ) {
809  $this->output( "Arguments:\n" );
810  // Arguments description
811  foreach ( $this->mArgList as $info ) {
812  $openChar = $info['require'] ? '<' : '[';
813  $closeChar = $info['require'] ? '>' : ']';
814  $this->output(
815  wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " .
816  $info['desc'], $descWidth, "\n$tab$tab" ) . "\n"
817  );
818  }
819  $this->output( "\n" );
820  }
821 
822  die( 1 );
823  }
824 
828  public function finalSetup() {
829  global $wgCommandLineMode, $wgShowSQLErrors, $wgServer;
830  global $wgDBadminuser, $wgDBadminpassword;
831  global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
832 
833  # Turn off output buffering again, it might have been turned on in the settings files
834  if ( ob_get_level() ) {
835  ob_end_flush();
836  }
837  # Same with these
838  $wgCommandLineMode = true;
839 
840  # Override $wgServer
841  if ( $this->hasOption( 'server' ) ) {
842  $wgServer = $this->getOption( 'server', $wgServer );
843  }
844 
845  # If these were passed, use them
846  if ( $this->mDbUser ) {
847  $wgDBadminuser = $this->mDbUser;
848  }
849  if ( $this->mDbPass ) {
850  $wgDBadminpassword = $this->mDbPass;
851  }
852 
853  if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) {
854  $wgDBuser = $wgDBadminuser;
855  $wgDBpassword = $wgDBadminpassword;
856 
857  if ( $wgDBservers ) {
861  foreach ( $wgDBservers as $i => $server ) {
862  $wgDBservers[$i]['user'] = $wgDBuser;
863  $wgDBservers[$i]['password'] = $wgDBpassword;
864  }
865  }
866  if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
867  $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
868  $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
869  }
871  }
872 
873  $this->afterFinalSetup();
874 
875  $wgShowSQLErrors = true;
876  @set_time_limit( 0 );
877  $this->adjustMemoryLimit();
878 
879  // Per-script profiling; useful for debugging
880  $forcedProfiler = $this->getOption( 'profiler' );
881  if ( $forcedProfiler === 'text' ) {
883  Profiler::instance()->setTemplated( true );
884  } elseif ( $forcedProfiler === 'trace' ) {
886  Profiler::instance()->setTemplated( true );
887  }
888  }
889 
893  protected function afterFinalSetup() {
894  if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
895  call_user_func( MW_CMDLINE_CALLBACK );
896  }
897  }
898 
903  public function globals() {
904  if ( $this->hasOption( 'globals' ) ) {
905  print_r( $GLOBALS );
906  }
907  }
908 
913  public function loadSettings() {
915 
916  if ( isset( $this->mOptions['conf'] ) ) {
917  $settingsFile = $this->mOptions['conf'];
918  } elseif ( defined( "MW_CONFIG_FILE" ) ) {
919  $settingsFile = MW_CONFIG_FILE;
920  } else {
921  $settingsFile = "$IP/LocalSettings.php";
922  }
923  if ( isset( $this->mOptions['wiki'] ) ) {
924  $bits = explode( '-', $this->mOptions['wiki'] );
925  if ( count( $bits ) == 1 ) {
926  $bits[] = '';
927  }
928  define( 'MW_DB', $bits[0] );
929  define( 'MW_PREFIX', $bits[1] );
930  }
931 
932  if ( !is_readable( $settingsFile ) ) {
933  $this->error( "A copy of your installation's LocalSettings.php\n" .
934  "must exist and be readable in the source directory.\n" .
935  "Use --conf to specify it.", true );
936  }
937  $wgCommandLineMode = true;
938  return $settingsFile;
939  }
940 
946  public function purgeRedundantText( $delete = true ) {
947  # Data should come off the master, wrapped in a transaction
948  $dbw = $this->getDB( DB_MASTER );
949  $dbw->begin( __METHOD__ );
950 
951  # Get "active" text records from the revisions table
952  $this->output( 'Searching for active text records in revisions table...' );
953  $res = $dbw->select( 'revision', 'rev_text_id', array(), __METHOD__, array( 'DISTINCT' ) );
954  foreach ( $res as $row ) {
955  $cur[] = $row->rev_text_id;
956  }
957  $this->output( "done.\n" );
958 
959  # Get "active" text records from the archive table
960  $this->output( 'Searching for active text records in archive table...' );
961  $res = $dbw->select( 'archive', 'ar_text_id', array(), __METHOD__, array( 'DISTINCT' ) );
962  foreach ( $res as $row ) {
963  # old pre-MW 1.5 records can have null ar_text_id's.
964  if ( $row->ar_text_id !== null ) {
965  $cur[] = $row->ar_text_id;
966  }
967  }
968  $this->output( "done.\n" );
969 
970  # Get the IDs of all text records not in these sets
971  $this->output( 'Searching for inactive text records...' );
972  $cond = 'old_id NOT IN ( ' . $dbw->makeList( $cur ) . ' )';
973  $res = $dbw->select( 'text', 'old_id', array( $cond ), __METHOD__, array( 'DISTINCT' ) );
974  $old = array();
975  foreach ( $res as $row ) {
976  $old[] = $row->old_id;
977  }
978  $this->output( "done.\n" );
979 
980  # Inform the user of what we're going to do
981  $count = count( $old );
982  $this->output( "$count inactive items found.\n" );
983 
984  # Delete as appropriate
985  if ( $delete && $count ) {
986  $this->output( 'Deleting...' );
987  $dbw->delete( 'text', array( 'old_id' => $old ), __METHOD__ );
988  $this->output( "done.\n" );
989  }
990 
991  # Done
992  $dbw->commit( __METHOD__ );
993  }
994 
999  protected function getDir() {
1000  return __DIR__;
1001  }
1002 
1010  protected function &getDB( $db, $groups = array(), $wiki = false ) {
1011  if ( is_null( $this->mDb ) ) {
1012  return wfGetDB( $db, $groups, $wiki );
1013  } else {
1014  return $this->mDb;
1015  }
1016  }
1017 
1023  public function setDB( &$db ) {
1024  $this->mDb = $db;
1025  }
1026 
1031  private function lockSearchindex( &$db ) {
1032  $write = array( 'searchindex' );
1033  $read = array( 'page', 'revision', 'text', 'interwiki', 'l10n_cache', 'user' );
1034  $db->lockTables( $read, $write, __CLASS__ . '::' . __METHOD__ );
1035  }
1036 
1041  private function unlockSearchindex( &$db ) {
1042  $db->unlockTables( __CLASS__ . '::' . __METHOD__ );
1043  }
1044 
1050  private function relockSearchindex( &$db ) {
1051  $this->unlockSearchindex( $db );
1052  $this->lockSearchindex( $db );
1053  }
1054 
1062  public function updateSearchIndex( $maxLockTime, $callback, $dbw, $results ) {
1063  $lockTime = time();
1064 
1065  # Lock searchindex
1066  if ( $maxLockTime ) {
1067  $this->output( " --- Waiting for lock ---" );
1068  $this->lockSearchindex( $dbw );
1069  $lockTime = time();
1070  $this->output( "\n" );
1071  }
1072 
1073  # Loop through the results and do a search update
1074  foreach ( $results as $row ) {
1075  # Allow reads to be processed
1076  if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
1077  $this->output( " --- Relocking ---" );
1078  $this->relockSearchindex( $dbw );
1079  $lockTime = time();
1080  $this->output( "\n" );
1081  }
1082  call_user_func( $callback, $dbw, $row );
1083  }
1084 
1085  # Unlock searchindex
1086  if ( $maxLockTime ) {
1087  $this->output( " --- Unlocking --" );
1088  $this->unlockSearchindex( $dbw );
1089  $this->output( "\n" );
1090  }
1091 
1092  }
1093 
1100  public function updateSearchIndexForPage( $dbw, $pageId ) {
1101  // Get current revision
1102  $rev = Revision::loadFromPageId( $dbw, $pageId );
1103  $title = null;
1104  if ( $rev ) {
1105  $titleObj = $rev->getTitle();
1106  $title = $titleObj->getPrefixedDBkey();
1107  $this->output( "$title..." );
1108  # Update searchindex
1109  $u = new SearchUpdate( $pageId, $titleObj->getText(), $rev->getContent() );
1110  $u->doUpdate();
1111  $this->output( "\n" );
1112  }
1113  return $title;
1114  }
1115 
1124  public static function posix_isatty( $fd ) {
1125  if ( !function_exists( 'posix_isatty' ) ) {
1126  return !$fd;
1127  } else {
1128  return posix_isatty( $fd );
1129  }
1130  }
1131 
1137  public static function readconsole( $prompt = '> ' ) {
1138  static $isatty = null;
1139  if ( is_null( $isatty ) ) {
1140  $isatty = self::posix_isatty( 0 /*STDIN*/ );
1141  }
1142 
1143  if ( $isatty && function_exists( 'readline' ) ) {
1144  return readline( $prompt );
1145  } else {
1146  if ( $isatty ) {
1147  $st = self::readlineEmulation( $prompt );
1148  } else {
1149  if ( feof( STDIN ) ) {
1150  $st = false;
1151  } else {
1152  $st = fgets( STDIN, 1024 );
1153  }
1154  }
1155  if ( $st === false ) {
1156  return false;
1157  }
1158  $resp = trim( $st );
1159  return $resp;
1160  }
1161  }
1162 
1168  private static function readlineEmulation( $prompt ) {
1169  $bash = Installer::locateExecutableInDefaultPaths( array( 'bash' ) );
1170  if ( !wfIsWindows() && $bash ) {
1171  $retval = false;
1172  $encPrompt = wfEscapeShellArg( $prompt );
1173  $command = "read -er -p $encPrompt && echo \"\$REPLY\"";
1174  $encCommand = wfEscapeShellArg( $command );
1175  $line = wfShellExec( "$bash -c $encCommand", $retval, array(), array( 'walltime' => 0 ) );
1176 
1177  if ( $retval == 0 ) {
1178  return $line;
1179  } elseif ( $retval == 127 ) {
1180  // Couldn't execute bash even though we thought we saw it.
1181  // Shell probably spit out an error message, sorry :(
1182  // Fall through to fgets()...
1183  } else {
1184  // EOF/ctrl+D
1185  return false;
1186  }
1187  }
1188 
1189  // Fallback... we'll have no editing controls, EWWW
1190  if ( feof( STDIN ) ) {
1191  return false;
1192  }
1193  print $prompt;
1194  return fgets( STDIN, 1024 );
1195  }
1196 }
1197 
1201 class FakeMaintenance extends Maintenance {
1202  protected $mSelf = "FakeMaintenanceScript";
1203  public function execute() {
1204  return;
1205  }
1206 }
1207 
1212 abstract class LoggedUpdateMaintenance extends Maintenance {
1213  public function __construct() {
1214  parent::__construct();
1215  $this->addOption( 'force', 'Run the update even if it was completed already' );
1216  $this->setBatchSize( 200 );
1217  }
1218 
1219  public function execute() {
1220  $db = $this->getDB( DB_MASTER );
1221  $key = $this->getUpdateKey();
1222 
1223  if ( !$this->hasOption( 'force' )
1224  && $db->selectRow( 'updatelog', '1', array( 'ul_key' => $key ), __METHOD__ )
1225  ) {
1226  $this->output( "..." . $this->updateSkippedMessage() . "\n" );
1227  return true;
1228  }
1229 
1230  if ( !$this->doDBUpdates() ) {
1231  return false;
1232  }
1233 
1234  if ( $db->insert( 'updatelog', array( 'ul_key' => $key ), __METHOD__, 'IGNORE' ) ) {
1235  return true;
1236  } else {
1237  $this->output( $this->updatelogFailedMessage() . "\n" );
1238  return false;
1239  }
1240  }
1241 
1246  protected function updateSkippedMessage() {
1247  $key = $this->getUpdateKey();
1248  return "Update '{$key}' already logged as completed.";
1249  }
1250 
1255  protected function updatelogFailedMessage() {
1256  $key = $this->getUpdateKey();
1257  return "Unable to log update '{$key}' as completed.";
1258  }
1259 
1265  abstract protected function doDBUpdates();
1266 
1271  abstract protected function getUpdateKey();
1272 }
FakeMaintenance\$mSelf
$mSelf
Definition: Maintenance.php:1199
RUN_MAINTENANCE_IF_MAIN
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:36
Maintenance\$mBatchSize
int $mBatchSize
Batch size.
Definition: Maintenance.php:97
LoggedUpdateMaintenance\__construct
__construct()
Default constructor.
Definition: Maintenance.php:1210
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
wfShellExec
wfShellExec( $cmd, &$retval=null, $environ=array(), $limits=array(), $options=array())
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
Definition: GlobalFunctions.php:2851
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
Maintenance\$mShortParamsMap
$mShortParamsMap
Definition: Maintenance.php:68
Maintenance\getStdin
getStdin( $len=null)
Return input from stdin.
Definition: Maintenance.php:287
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:127
Maintenance\getDbType
getDbType()
Does the script need different DB access? By default, we give Maintenance scripts normal rights to th...
Definition: Maintenance.php:399
Maintenance\maybeHelp
maybeHelp( $force=false)
Maybe show the help.
Definition: Maintenance.php:710
Maintenance\$mDbPass
$mDbPass
Definition: Maintenance.php:84
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3706
Maintenance\addDescription
addDescription( $text)
Set the description text.
Definition: Maintenance.php:227
Maintenance\setup
setup()
Do some sanity checking and basic setup.
Definition: Maintenance.php:469
$f
$f
Definition: UtfNormalTest2.php:38
Maintenance\runChild
runChild( $maintClass, $classFile=null)
Run a child maintenance script.
Definition: Maintenance.php:444
Maintenance\readconsole
static readconsole( $prompt='> ')
Prompt the console for input.
Definition: Maintenance.php:1134
Maintenance\addOption
addOption( $name, $description, $required=false, $withArg=false, $shortName=false)
Add a parameter to the script.
Definition: Maintenance.php:169
Maintenance\loadParamsAndArgs
loadParamsAndArgs( $self=null, $opts=null, $args=null)
Process command line arguments $mOptions becomes an array with keys set to the option names $mArgs be...
Definition: Maintenance.php:567
wfPHPVersionError
wfPHPVersionError( $type)
Display something vaguely comprehensible in the event of a totally unrecoverable error.
Definition: PHPVersionError.php:40
Maintenance\unlockSearchindex
unlockSearchindex(&$db)
Unlock the tables.
Definition: Maintenance.php:1038
$limit
if( $sleep) $limit
Definition: importImages.php:99
Maintenance\getName
getName()
Get the script's name.
Definition: Maintenance.php:276
$s
$s
Definition: mergeMessageFileList.php:156
Maintenance\hasArg
hasArg( $argId=0)
Does a given argument exist?
Definition: Maintenance.php:236
Maintenance
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: maintenance.txt:39
$maintClass
$maintClass
Definition: Maintenance.php:39
Maintenance\$lastChannel
$lastChannel
Definition: Maintenance.php:347
ProfilerSimpleText
The least sophisticated profiler output class possible, view your source! :)
Definition: ProfilerSimpleText.php:34
Installer\locateExecutableInDefaultPaths
static locateExecutableInDefaultPaths( $names, $versionInfo=false)
Same as locateExecutable(), but checks in getPossibleBinPaths() by default.
Definition: Installer.php:1288
Maintenance\finalSetup
finalSetup()
Handle some last-minute setup here.
Definition: Maintenance.php:825
Maintenance\relockSearchindex
relockSearchindex(&$db)
Unlock and lock again Since the lock is low-priority, queued reads will be able to complete.
Definition: Maintenance.php:1047
Maintenance\setDB
setDB(&$db)
Sets database object to be returned by getDB().
Definition: Maintenance.php:1020
Maintenance\$mDescription
$mDescription
Definition: Maintenance.php:87
Maintenance\afterFinalSetup
afterFinalSetup()
Execute a callback function at the end of initialisation.
Definition: Maintenance.php:890
Maintenance\clearParamsAndArgs
clearParamsAndArgs()
Clear all params and arguments.
Definition: Maintenance.php:552
Maintenance\getDB
& getDB( $db, $groups=array(), $wiki=false)
Returns a database to be used by current maintenance script.
Definition: Maintenance.php:1007
Maintenance\$mSelf
$mSelf
Definition: Maintenance.php:80
Maintenance\updateSearchIndex
updateSearchIndex( $maxLockTime, $callback, $dbw, $results)
Perform a search index update with locking.
Definition: Maintenance.php:1059
Maintenance\lockSearchindex
lockSearchindex(&$db)
Lock the search index.
Definition: Maintenance.php:1028
Maintenance\$mArgs
$mArgs
Definition: Maintenance.php:77
Maintenance::__construct
public function __construct()
Definition: maintenance.txt:41
$out
$out
Definition: UtfNormalGenerate.php:167
Maintenance\loadSpecialVars
loadSpecialVars()
Handle the special variables that are global to all scripts.
Definition: Maintenance.php:691
LoggedUpdateMaintenance
Class for scripts that perform database maintenance and want to log the update in updatelog so we can...
Definition: Maintenance.php:1209
Maintenance\$atLineStart
$atLineStart
Definition: Maintenance.php:346
$wgCommandLineMode
global $wgCommandLineMode
Definition: Setup.php:408
$wgRequestTime
$wgRequestTime
Definition: WebStart.php:68
Revision\loadFromPageId
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition: Revision.php:232
Maintenance\$mDbUser
$mDbUser
Definition: Maintenance.php:84
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
Maintenance\globals
globals()
Potentially debug globals.
Definition: Maintenance.php:900
LoggedUpdateMaintenance\updatelogFailedMessage
updatelogFailedMessage()
Message to show the the update log was unable to log the completion of this update.
Definition: Maintenance.php:1252
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
FakeMaintenance
Fake maintenance wrapper, mostly used for the web installer/updater.
Definition: Maintenance.php:1198
Maintenance\addDefaultParams
addDefaultParams()
Add the default parameters to the scripts.
Definition: Maintenance.php:406
Maintenance\readlineEmulation
static readlineEmulation( $prompt)
Emulate readline()
Definition: Maintenance.php:1165
ProfilerSimpleTrace
Execution trace.
Definition: ProfilerSimpleTrace.php:29
Maintenance\DB_ADMIN
const DB_ADMIN
Definition: Maintenance.php:59
Maintenance\$mGenericParameters
$mGenericParameters
Definition: Maintenance.php:100
Maintenance\deleteOption
deleteOption( $name)
Remove an option.
Definition: Maintenance.php:219
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
Maintenance\shouldExecute
static shouldExecute()
Should we execute the maintenance script, or just allow it to be included as a standalone class?...
Definition: Maintenance.php:136
$command
$command
Definition: cdb.php:63
$line
$line
Definition: cdb.php:57
LoggedUpdateMaintenance\execute
execute()
Do the actual work.
Definition: Maintenance.php:1216
Maintenance\DB_STD
const DB_STD
Definition: Maintenance.php:58
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
Maintenance\updateSearchIndexForPage
updateSearchIndexForPage( $dbw, $pageId)
Update the searchindex table for a given pageid.
Definition: Maintenance.php:1097
Maintenance\$mQuiet
$mQuiet
Definition: Maintenance.php:83
Maintenance\isQuiet
isQuiet()
Definition: Maintenance.php:303
Profiler\setInstance
static setInstance(Profiler $p)
Set the profiler to a specific profiler instance.
Definition: Profiler.php:150
DatabaseBase
Database abstraction object.
Definition: Database.php:219
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2571
Maintenance\$mParams
$mParams
Definition: Maintenance.php:65
wfEscapeShellArg
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
Definition: GlobalFunctions.php:2752
Maintenance\posix_isatty
static posix_isatty( $fd)
Wrapper for posix_isatty() We default as considering stdin a tty (for nice readline methods) but trea...
Definition: Maintenance.php:1121
LoggedUpdateMaintenance\updateSkippedMessage
updateSkippedMessage()
Message to show that the update was done already and was just skipped.
Definition: Maintenance.php:1243
Maintenance\$mDependantParameters
$mDependantParameters
Definition: Maintenance.php:102
Maintenance\validateParamsAndArgs
validateParamsAndArgs()
Run some validation checks on the params, etc.
Definition: Maintenance.php:666
Maintenance\cleanupChanneled
cleanupChanneled()
Clean up channeled output.
Definition: Maintenance.php:352
Maintenance\DB_NONE
const DB_NONE
Constants for DB access type.
Definition: Maintenance.php:57
Maintenance\loadSettings
loadSettings()
Generic setup for most installs.
Definition: Maintenance.php:910
LoggedUpdateMaintenance\doDBUpdates
doDBUpdates()
Do the actual work.
Maintenance\adjustMemoryLimit
adjustMemoryLimit()
Adjusts PHP's memory limit to better suit our needs, if needed.
Definition: Maintenance.php:539
LoggedUpdateMaintenance\getUpdateKey
getUpdateKey()
Get the update key name to go in the update log table.
Maintenance\purgeRedundantText
purgeRedundantText( $delete=true)
Support function for cleaning up redundant text records.
Definition: Maintenance.php:943
$count
$count
Definition: UtfNormalTest2.php:96
$self
$self
Definition: doMaintenance.php:54
$rev
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1337
Maintenance\$fileHandle
resource $fileHandle
Used when creating separate schema files.
Definition: Maintenance.php:112
Maintenance\$mDb
DatabaseBase $mDb
Used by getDD() / setDB()
Definition: Maintenance.php:107
$args
if( $line===false) $args
Definition: cdb.php:62
Maintenance\$mInputLoaded
$mInputLoaded
Definition: Maintenance.php:90
Maintenance\getOption
getOption( $name, $default=null)
Get an option, or return the default.
Definition: Maintenance.php:191
Maintenance\STDIN_ALL
const STDIN_ALL
Definition: Maintenance.php:62
$output
& $output
Definition: hooks.txt:375
Maintenance\addArg
addArg( $arg, $description, $required=true)
Add some args that are needed.
Definition: Maintenance.php:207
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Maintenance\$mOptions
$mOptions
Definition: Maintenance.php:74
Maintenance\error
error( $err, $die=0)
Throw an error to the user.
Definition: Maintenance.php:333
Maintenance\output
output( $out, $channel=null)
Throw some output to the user.
Definition: Maintenance.php:314
Maintenance\$mArgList
$mArgList
Definition: Maintenance.php:71
Maintenance\getDir
getDir()
Get the maintenance directory.
Definition: Maintenance.php:996
Maintenance\hasOption
hasOption( $name)
Checks to see if a particular param exists.
Definition: Maintenance.php:181
Maintenance\getArg
getArg( $argId=0, $default=null)
Get an argument.
Definition: Maintenance.php:246
Maintenance::execute
public function execute()
Definition: maintenance.txt:45
$IP
$IP
Definition: WebStart.php:88
Maintenance\outputChanneled
outputChanneled( $msg, $channel=null)
Message outputter with channeled message support.
Definition: Maintenance.php:367
$res
$res
Definition: database.txt:21
Maintenance\__construct
__construct()
Default constructor.
Definition: Maintenance.php:118
$retval
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account incomplete not yet checked for validity & $retval
Definition: hooks.txt:237
FakeMaintenance\execute
execute()
Do the actual work.
Definition: Maintenance.php:1200
$GLOBALS
$GLOBALS['IP']
Definition: ComposerHookHandler.php:6
Maintenance\setBatchSize
setBatchSize( $s=0)
Set the batch size.
Definition: Maintenance.php:254
LBFactory\destroyInstance
static destroyInstance()
Shut down, close connections and destroy the cached instance.
Definition: LBFactory.php:90
Maintenance\memoryLimit
memoryLimit()
Normally we disable the memory_limit when running admin scripts.
Definition: Maintenance.php:530