MediaWiki REL1_34
Maintenance.php
Go to the documentation of this file.
1<?php
23define( 'MW_ENTRY_POINT', 'cli' );
24
25// Bail on old versions of PHP, or if composer has not been run yet to install
26// dependencies.
27require_once __DIR__ . '/../includes/PHPVersionCheck.php';
28wfEntryPointCheck( 'text' );
29
32
38// Define this so scripts can easily find doMaintenance.php
39define( 'RUN_MAINTENANCE_IF_MAIN', __DIR__ . '/doMaintenance.php' );
40
44define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
45
47
48// Some extensions rely on MW_INSTALL_PATH to find core files to include. Setting it here helps them
49// if they're included by a core script (like DatabaseUpdater) after Maintenance.php has already
50// been run.
51if ( strval( getenv( 'MW_INSTALL_PATH' ) ) === '' ) {
52 putenv( 'MW_INSTALL_PATH=' . realpath( __DIR__ . '/..' ) );
53}
54
60
82abstract class Maintenance {
87 const DB_NONE = 0;
88 const DB_STD = 1;
89 const DB_ADMIN = 2;
90
91 // Const for getStdin()
92 const STDIN_ALL = 'all';
93
99 protected $mParams = [];
100
101 // Array of mapping short parameters to long ones
102 protected $mShortParamsMap = [];
103
104 // Array of desired/allowed args
105 protected $mArgList = [];
106
107 // This is the list of options that were actually passed
108 protected $mOptions = [];
109
110 // This is the list of arguments that were actually passed
111 protected $mArgs = [];
112
113 // Allow arbitrary options to be passed, or only specified ones?
114 protected $mAllowUnregisteredOptions = false;
115
116 // Name of the script currently running
117 protected $mSelf;
118
119 // Special vars for params that are always used
120 protected $mQuiet = false;
121 protected $mDbUser, $mDbPass;
122
123 // A description of the script, children should change this via addDescription()
124 protected $mDescription = '';
125
126 // Have we already loaded our user input?
127 protected $mInputLoaded = false;
128
135 protected $mBatchSize = null;
136
149
154 private $mDb = null;
155
157 private $lastReplicationWait = 0.0;
158
164
170 private $config;
171
177
189 public $orderedOptions = [];
190
195 public function __construct() {
196 global $IP;
197 $IP = getenv( 'MW_INSTALL_PATH' );
198
199 $this->addDefaultParams();
200 register_shutdown_function( [ $this, 'outputChanneled' ], false );
201 }
202
210 public static function shouldExecute() {
211 global $wgCommandLineMode;
212
213 if ( !function_exists( 'debug_backtrace' ) ) {
214 // If someone has a better idea...
215 return $wgCommandLineMode;
216 }
217
218 $bt = debug_backtrace();
219 $count = count( $bt );
220 if ( $count < 2 ) {
221 return false; // sanity
222 }
223 if ( $bt[0]['class'] !== self::class || $bt[0]['function'] !== 'shouldExecute' ) {
224 return false; // last call should be to this function
225 }
226 $includeFuncs = [ 'require_once', 'require', 'include', 'include_once' ];
227 for ( $i = 1; $i < $count; $i++ ) {
228 if ( !in_array( $bt[$i]['function'], $includeFuncs ) ) {
229 return false; // previous calls should all be "requires"
230 }
231 }
232
233 return true;
234 }
235
244 abstract public function execute();
245
252 protected function supportsOption( $name ) {
253 return isset( $this->mParams[$name] );
254 }
255
267 protected function addOption( $name, $description, $required = false,
268 $withArg = false, $shortName = false, $multiOccurrence = false
269 ) {
270 $this->mParams[$name] = [
271 'desc' => $description,
272 'require' => $required,
273 'withArg' => $withArg,
274 'shortName' => $shortName,
275 'multiOccurrence' => $multiOccurrence
276 ];
277
278 if ( $shortName !== false ) {
279 $this->mShortParamsMap[$shortName] = $name;
280 }
281 }
282
288 protected function hasOption( $name ) {
289 return isset( $this->mOptions[$name] );
290 }
291
302 protected function getOption( $name, $default = null ) {
303 if ( $this->hasOption( $name ) ) {
304 return $this->mOptions[$name];
305 } else {
306 // Set it so we don't have to provide the default again
307 $this->mOptions[$name] = $default;
308
309 return $this->mOptions[$name];
310 }
311 }
312
319 protected function addArg( $arg, $description, $required = true ) {
320 $this->mArgList[] = [
321 'name' => $arg,
322 'desc' => $description,
323 'require' => $required
324 ];
325 }
326
331 protected function deleteOption( $name ) {
332 unset( $this->mParams[$name] );
333 }
334
340 protected function setAllowUnregisteredOptions( $allow ) {
341 $this->mAllowUnregisteredOptions = $allow;
342 }
343
348 protected function addDescription( $text ) {
349 $this->mDescription = $text;
350 }
351
357 protected function hasArg( $argId = 0 ) {
358 if ( func_num_args() === 0 ) {
359 wfDeprecated( __METHOD__ . ' without an $argId', '1.33' );
360 }
361
362 return isset( $this->mArgs[$argId] );
363 }
364
371 protected function getArg( $argId = 0, $default = null ) {
372 if ( func_num_args() === 0 ) {
373 wfDeprecated( __METHOD__ . ' without an $argId', '1.33' );
374 }
375
376 return $this->mArgs[$argId] ?? $default;
377 }
378
386 protected function getBatchSize() {
387 return $this->mBatchSize;
388 }
389
394 protected function setBatchSize( $s = 0 ) {
395 $this->mBatchSize = $s;
396
397 // If we support $mBatchSize, show the option.
398 // Used to be in addDefaultParams, but in order for that to
399 // work, subclasses would have to call this function in the constructor
400 // before they called parent::__construct which is just weird
401 // (and really wasn't done).
402 if ( $this->mBatchSize ) {
403 $this->addOption( 'batch-size', 'Run this many operations ' .
404 'per batch, default: ' . $this->mBatchSize, false, true );
405 if ( isset( $this->mParams['batch-size'] ) ) {
406 // This seems a little ugly...
407 $this->mDependantParameters['batch-size'] = $this->mParams['batch-size'];
408 }
409 }
410 }
411
416 public function getName() {
417 return $this->mSelf;
418 }
419
426 protected function getStdin( $len = null ) {
427 if ( $len == self::STDIN_ALL ) {
428 return file_get_contents( 'php://stdin' );
429 }
430 $f = fopen( 'php://stdin', 'rt' );
431 if ( !$len ) {
432 return $f;
433 }
434 $input = fgets( $f, $len );
435 fclose( $f );
436
437 return rtrim( $input );
438 }
439
443 public function isQuiet() {
444 return $this->mQuiet;
445 }
446
453 protected function output( $out, $channel = null ) {
454 // This is sometimes called very early, before Setup.php is included.
455 if ( class_exists( MediaWikiServices::class ) ) {
456 // Try to periodically flush buffered metrics to avoid OOMs
457 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
458 if ( $stats->getDataCount() > 1000 ) {
459 MediaWiki::emitBufferedStatsdData( $stats, $this->getConfig() );
460 }
461 }
462
463 if ( $this->mQuiet ) {
464 return;
465 }
466 if ( $channel === null ) {
467 $this->cleanupChanneled();
468 print $out;
469 } else {
470 $out = preg_replace( '/\n\z/', '', $out );
471 $this->outputChanneled( $out, $channel );
472 }
473 }
474
481 protected function error( $err, $die = 0 ) {
482 if ( intval( $die ) !== 0 ) {
483 wfDeprecated( __METHOD__ . '( $err, $die )', '1.31' );
484 $this->fatalError( $err, intval( $die ) );
485 }
486 $this->outputChanneled( false );
487 if (
488 ( PHP_SAPI == 'cli' || PHP_SAPI == 'phpdbg' ) &&
489 !defined( 'MW_PHPUNIT_TEST' )
490 ) {
491 fwrite( STDERR, $err . "\n" );
492 } else {
493 print $err;
494 }
495 }
496
504 protected function fatalError( $msg, $exitCode = 1 ) {
505 $this->error( $msg );
506 exit( $exitCode );
507 }
508
509 private $atLineStart = true;
510 private $lastChannel = null;
511
515 public function cleanupChanneled() {
516 if ( !$this->atLineStart ) {
517 print "\n";
518 $this->atLineStart = true;
519 }
520 }
521
530 public function outputChanneled( $msg, $channel = null ) {
531 if ( $msg === false ) {
532 $this->cleanupChanneled();
533
534 return;
535 }
536
537 // End the current line if necessary
538 if ( !$this->atLineStart && $channel !== $this->lastChannel ) {
539 print "\n";
540 }
541
542 print $msg;
543
544 $this->atLineStart = false;
545 if ( $channel === null ) {
546 // For unchanneled messages, output trailing newline immediately
547 print "\n";
548 $this->atLineStart = true;
549 }
550 $this->lastChannel = $channel;
551 }
552
563 public function getDbType() {
564 return self::DB_STD;
565 }
566
570 protected function addDefaultParams() {
571 # Generic (non script dependant) options:
572
573 $this->addOption( 'help', 'Display this help message', false, false, 'h' );
574 $this->addOption( 'quiet', 'Whether to suppress non-error output', false, false, 'q' );
575 $this->addOption( 'conf', 'Location of LocalSettings.php, if not default', false, true );
576 $this->addOption( 'wiki', 'For specifying the wiki ID', false, true );
577 $this->addOption( 'globals', 'Output globals at the end of processing for debugging' );
578 $this->addOption(
579 'memory-limit',
580 'Set a specific memory limit for the script, '
581 . '"max" for no limit or "default" to avoid changing it',
582 false,
583 true
584 );
585 $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " .
586 "http://en.wikipedia.org. This is sometimes necessary because " .
587 "server name detection may fail in command line scripts.", false, true );
588 $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true );
589 // This is named --mwdebug, because --debug would conflict in the phpunit.php CLI script.
590 $this->addOption( 'mwdebug', 'Enable built-in MediaWiki development settings', false, false );
591
592 # Save generic options to display them separately in help
593 $this->mGenericParameters = $this->mParams;
594
595 # Script dependant options:
596
597 // If we support a DB, show the options
598 if ( $this->getDbType() > 0 ) {
599 $this->addOption( 'dbuser', 'The DB user to use for this script', false, true );
600 $this->addOption( 'dbpass', 'The password to use for this script', false, true );
601 $this->addOption( 'dbgroupdefault', 'The default DB group to use.', false, true );
602 }
603
604 # Save additional script dependant options to display
605 #  them separately in help
606 $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters );
607 }
608
613 public function getConfig() {
614 if ( $this->config === null ) {
615 $this->config = MediaWikiServices::getInstance()->getMainConfig();
616 }
617
618 return $this->config;
619 }
620
625 public function setConfig( Config $config ) {
626 $this->config = $config;
627 }
628
638 protected function requireExtension( $name ) {
639 $this->requiredExtensions[] = $name;
640 }
641
647 public function checkRequiredExtensions() {
648 $registry = ExtensionRegistry::getInstance();
649 $missing = [];
650 foreach ( $this->requiredExtensions as $name ) {
651 if ( !$registry->isLoaded( $name ) ) {
652 $missing[] = $name;
653 }
654 }
655
656 if ( $missing ) {
657 $joined = implode( ', ', $missing );
658 $msg = "The following extensions are required to be installed "
659 . "for this script to run: $joined. Please enable them and then try again.";
660 $this->fatalError( $msg );
661 }
662 }
663
668 public function setAgentAndTriggers() {
669 if ( function_exists( 'posix_getpwuid' ) ) {
670 $agent = posix_getpwuid( posix_geteuid() )['name'];
671 } else {
672 $agent = 'sysadmin';
673 }
674 $agent .= '@' . wfHostname();
675
676 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
677 // Add a comment for easy SHOW PROCESSLIST interpretation
678 $lbFactory->setAgentName(
679 mb_strlen( $agent ) > 15 ? mb_substr( $agent, 0, 15 ) . '...' : $agent
680 );
681 self::setLBFactoryTriggers( $lbFactory, $this->getConfig() );
682 }
683
689 public static function setLBFactoryTriggers( LBFactory $LBFactory, Config $config ) {
690 $services = MediaWikiServices::getInstance();
691 $stats = $services->getStatsdDataFactory();
692 // Hook into period lag checks which often happen in long-running scripts
693 $lbFactory = $services->getDBLoadBalancerFactory();
694 $lbFactory->setWaitForReplicationListener(
695 __METHOD__,
696 function () use ( $stats, $config ) {
697 // Check config in case of JobRunner and unit tests
698 if ( $config->get( 'CommandLineMode' ) ) {
699 DeferredUpdates::tryOpportunisticExecute( 'run' );
700 }
701 // Try to periodically flush buffered metrics to avoid OOMs
702 MediaWiki::emitBufferedStatsdData( $stats, $config );
703 }
704 );
705 // Check for other windows to run them. A script may read or do a few writes
706 // to the master but mostly be writing to something else, like a file store.
707 $lbFactory->getMainLB()->setTransactionListener(
708 __METHOD__,
709 function ( $trigger ) use ( $stats, $config ) {
710 // Check config in case of JobRunner and unit tests
711 if ( $config->get( 'CommandLineMode' ) && $trigger === IDatabase::TRIGGER_COMMIT ) {
712 DeferredUpdates::tryOpportunisticExecute( 'run' );
713 }
714 // Try to periodically flush buffered metrics to avoid OOMs
715 MediaWiki::emitBufferedStatsdData( $stats, $config );
716 }
717 );
718 }
719
727 public function runChild( $maintClass, $classFile = null ) {
728 // Make sure the class is loaded first
729 if ( !class_exists( $maintClass ) ) {
730 if ( $classFile ) {
731 require_once $classFile;
732 }
733 if ( !class_exists( $maintClass ) ) {
734 $this->error( "Cannot spawn child: $maintClass" );
735 }
736 }
737
741 $child = new $maintClass();
742 $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
743 if ( !is_null( $this->mDb ) ) {
744 $child->setDB( $this->mDb );
745 }
746
747 return $child;
748 }
749
753 public function setup() {
754 global $IP, $wgCommandLineMode;
755
756 # Abort if called from a web server
757 # wfIsCLI() is not available yet
758 if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
759 $this->fatalError( 'This script must be run from the command line' );
760 }
761
762 if ( $IP === null ) {
763 $this->fatalError( "\$IP not set, aborting!\n" .
764 '(Did you forget to call parent::__construct() in your maintenance script?)' );
765 }
766
767 # Make sure we can handle script parameters
768 if ( !defined( 'HPHP_VERSION' ) && !ini_get( 'register_argc_argv' ) ) {
769 $this->fatalError( 'Cannot get command line arguments, register_argc_argv is set to false' );
770 }
771
772 // Send PHP warnings and errors to stderr instead of stdout.
773 // This aids in diagnosing problems, while keeping messages
774 // out of redirected output.
775 if ( ini_get( 'display_errors' ) ) {
776 ini_set( 'display_errors', 'stderr' );
777 }
778
779 $this->loadParamsAndArgs();
780
781 # Set the memory limit
782 # Note we need to set it again later in cache LocalSettings changed it
783 $this->adjustMemoryLimit();
784
785 # Set max execution time to 0 (no limit). PHP.net says that
786 # "When running PHP from the command line the default setting is 0."
787 # But sometimes this doesn't seem to be the case.
788 ini_set( 'max_execution_time', 0 );
789
790 # Define us as being in MediaWiki
791 define( 'MEDIAWIKI', true );
792
793 $wgCommandLineMode = true;
794
795 # Turn off output buffering if it's on
796 while ( ob_get_level() > 0 ) {
797 ob_end_flush();
798 }
799 }
800
810 public function memoryLimit() {
811 $limit = $this->getOption( 'memory-limit', 'max' );
812 $limit = trim( $limit, "\" '" ); // trim quotes in case someone misunderstood
813 return $limit;
814 }
815
819 protected function adjustMemoryLimit() {
820 $limit = $this->memoryLimit();
821 if ( $limit == 'max' ) {
822 $limit = -1; // no memory limit
823 }
824 if ( $limit != 'default' ) {
825 ini_set( 'memory_limit', $limit );
826 }
827 }
828
832 protected function activateProfiler() {
834
835 $output = $this->getOption( 'profiler' );
836 if ( !$output ) {
837 return;
838 }
839
840 if ( is_array( $wgProfiler ) && isset( $wgProfiler['class'] ) ) {
841 $class = $wgProfiler['class'];
843 $profiler = new $class(
844 [ 'sampling' => 1, 'output' => [ $output ] ]
846 + [ 'threshold' => $wgProfileLimit ]
847 );
848 $profiler->setAllowOutput();
849 Profiler::replaceStubInstance( $profiler );
850 }
851
852 $trxProfiler = Profiler::instance()->getTransactionProfiler();
853 $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
854 $trxProfiler->setExpectations( $wgTrxProfilerLimits['Maintenance'], __METHOD__ );
855 }
856
860 public function clearParamsAndArgs() {
861 $this->mOptions = [];
862 $this->mArgs = [];
863 $this->mInputLoaded = false;
864 }
865
873 public function loadWithArgv( $argv ) {
874 $options = [];
875 $args = [];
876 $this->orderedOptions = [];
877
878 # Parse arguments
879 for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
880 if ( $arg == '--' ) {
881 # End of options, remainder should be considered arguments
882 $arg = next( $argv );
883 while ( $arg !== false ) {
884 $args[] = $arg;
885 $arg = next( $argv );
886 }
887 break;
888 } elseif ( substr( $arg, 0, 2 ) == '--' ) {
889 # Long options
890 $option = substr( $arg, 2 );
891 if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
892 $param = next( $argv );
893 if ( $param === false ) {
894 $this->error( "\nERROR: $option parameter needs a value after it\n" );
895 $this->maybeHelp( true );
896 }
897
898 $this->setParam( $options, $option, $param );
899 } else {
900 $bits = explode( '=', $option, 2 );
901 $this->setParam( $options, $bits[0], $bits[1] ?? 1 );
902 }
903 } elseif ( $arg == '-' ) {
904 # Lonely "-", often used to indicate stdin or stdout.
905 $args[] = $arg;
906 } elseif ( substr( $arg, 0, 1 ) == '-' ) {
907 # Short options
908 $argLength = strlen( $arg );
909 for ( $p = 1; $p < $argLength; $p++ ) {
910 $option = $arg[$p];
911 if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) {
912 $option = $this->mShortParamsMap[$option];
913 }
914
915 if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
916 $param = next( $argv );
917 if ( $param === false ) {
918 $this->error( "\nERROR: $option parameter needs a value after it\n" );
919 $this->maybeHelp( true );
920 }
921 $this->setParam( $options, $option, $param );
922 } else {
923 $this->setParam( $options, $option, 1 );
924 }
925 }
926 } else {
927 $args[] = $arg;
928 }
929 }
930
931 $this->mOptions = $options;
932 $this->mArgs = $args;
933 $this->loadSpecialVars();
934 $this->mInputLoaded = true;
935 }
936
949 private function setParam( &$options, $option, $value ) {
950 $this->orderedOptions[] = [ $option, $value ];
951
952 if ( isset( $this->mParams[$option] ) ) {
953 $multi = $this->mParams[$option]['multiOccurrence'];
954 } else {
955 $multi = false;
956 }
957 $exists = array_key_exists( $option, $options );
958 if ( $multi && $exists ) {
959 $options[$option][] = $value;
960 } elseif ( $multi ) {
961 $options[$option] = [ $value ];
962 } elseif ( !$exists ) {
963 $options[$option] = $value;
964 } else {
965 $this->error( "\nERROR: $option parameter given twice\n" );
966 $this->maybeHelp( true );
967 }
968 }
969
979 public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
980 # If we were given opts or args, set those and return early
981 if ( $self ) {
982 $this->mSelf = $self;
983 $this->mInputLoaded = true;
984 }
985 if ( $opts ) {
986 $this->mOptions = $opts;
987 $this->mInputLoaded = true;
988 }
989 if ( $args ) {
990 $this->mArgs = $args;
991 $this->mInputLoaded = true;
992 }
993
994 # If we've already loaded input (either by user values or from $argv)
995 # skip on loading it again. The array_shift() will corrupt values if
996 # it's run again and again
997 if ( $this->mInputLoaded ) {
998 $this->loadSpecialVars();
999
1000 return;
1001 }
1002
1003 global $argv;
1004 $this->mSelf = $argv[0];
1005 $this->loadWithArgv( array_slice( $argv, 1 ) );
1006 }
1007
1011 public function validateParamsAndArgs() {
1012 $die = false;
1013 # Check to make sure we've got all the required options
1014 foreach ( $this->mParams as $opt => $info ) {
1015 if ( $info['require'] && !$this->hasOption( $opt ) ) {
1016 $this->error( "Param $opt required!" );
1017 $die = true;
1018 }
1019 }
1020 # Check arg list too
1021 foreach ( $this->mArgList as $k => $info ) {
1022 if ( $info['require'] && !$this->hasArg( $k ) ) {
1023 $this->error( 'Argument <' . $info['name'] . '> required!' );
1024 $die = true;
1025 }
1026 }
1027 if ( !$this->mAllowUnregisteredOptions ) {
1028 # Check for unexpected options
1029 foreach ( $this->mOptions as $opt => $val ) {
1030 if ( !$this->supportsOption( $opt ) ) {
1031 $this->error( "Unexpected option $opt!" );
1032 $die = true;
1033 }
1034 }
1035 }
1036
1037 $this->maybeHelp( $die );
1038 }
1039
1043 protected function loadSpecialVars() {
1044 if ( $this->hasOption( 'dbuser' ) ) {
1045 $this->mDbUser = $this->getOption( 'dbuser' );
1046 }
1047 if ( $this->hasOption( 'dbpass' ) ) {
1048 $this->mDbPass = $this->getOption( 'dbpass' );
1049 }
1050 if ( $this->hasOption( 'quiet' ) ) {
1051 $this->mQuiet = true;
1052 }
1053 if ( $this->hasOption( 'batch-size' ) ) {
1054 $this->mBatchSize = intval( $this->getOption( 'batch-size' ) );
1055 }
1056 }
1057
1062 protected function maybeHelp( $force = false ) {
1063 if ( !$force && !$this->hasOption( 'help' ) ) {
1064 return;
1065 }
1066
1067 $screenWidth = 80; // TODO: Calculate this!
1068 $tab = " ";
1069 $descWidth = $screenWidth - ( 2 * strlen( $tab ) );
1070
1071 ksort( $this->mParams );
1072 $this->mQuiet = false;
1073
1074 // Description ...
1075 if ( $this->mDescription ) {
1076 $this->output( "\n" . wordwrap( $this->mDescription, $screenWidth ) . "\n" );
1077 }
1078 $output = "\nUsage: php " . basename( $this->mSelf );
1079
1080 // ... append parameters ...
1081 if ( $this->mParams ) {
1082 $output .= " [--" . implode( "|--", array_keys( $this->mParams ) ) . "]";
1083 }
1084
1085 // ... and append arguments.
1086 if ( $this->mArgList ) {
1087 $output .= ' ';
1088 foreach ( $this->mArgList as $k => $arg ) {
1089 if ( $arg['require'] ) {
1090 $output .= '<' . $arg['name'] . '>';
1091 } else {
1092 $output .= '[' . $arg['name'] . ']';
1093 }
1094 if ( $k < count( $this->mArgList ) - 1 ) {
1095 $output .= ' ';
1096 }
1097 }
1098 }
1099 $this->output( "$output\n\n" );
1100
1101 # TODO abstract some repetitive code below
1102
1103 // Generic parameters
1104 $this->output( "Generic maintenance parameters:\n" );
1105 foreach ( $this->mGenericParameters as $par => $info ) {
1106 if ( $info['shortName'] !== false ) {
1107 $par .= " (-{$info['shortName']})";
1108 }
1109 $this->output(
1110 wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1111 "\n$tab$tab" ) . "\n"
1112 );
1113 }
1114 $this->output( "\n" );
1115
1116 $scriptDependantParams = $this->mDependantParameters;
1117 if ( count( $scriptDependantParams ) > 0 ) {
1118 $this->output( "Script dependant parameters:\n" );
1119 // Parameters description
1120 foreach ( $scriptDependantParams as $par => $info ) {
1121 if ( $info['shortName'] !== false ) {
1122 $par .= " (-{$info['shortName']})";
1123 }
1124 $this->output(
1125 wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1126 "\n$tab$tab" ) . "\n"
1127 );
1128 }
1129 $this->output( "\n" );
1130 }
1131
1132 // Script specific parameters not defined on construction by
1133 // Maintenance::addDefaultParams()
1134 $scriptSpecificParams = array_diff_key(
1135 # all script parameters:
1137 # remove the Maintenance default parameters:
1140 );
1141 if ( count( $scriptSpecificParams ) > 0 ) {
1142 $this->output( "Script specific parameters:\n" );
1143 // Parameters description
1144 foreach ( $scriptSpecificParams as $par => $info ) {
1145 if ( $info['shortName'] !== false ) {
1146 $par .= " (-{$info['shortName']})";
1147 }
1148 $this->output(
1149 wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1150 "\n$tab$tab" ) . "\n"
1151 );
1152 }
1153 $this->output( "\n" );
1154 }
1155
1156 // Print arguments
1157 if ( count( $this->mArgList ) > 0 ) {
1158 $this->output( "Arguments:\n" );
1159 // Arguments description
1160 foreach ( $this->mArgList as $info ) {
1161 $openChar = $info['require'] ? '<' : '[';
1162 $closeChar = $info['require'] ? '>' : ']';
1163 $this->output(
1164 wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " .
1165 $info['desc'], $descWidth, "\n$tab$tab" ) . "\n"
1166 );
1167 }
1168 $this->output( "\n" );
1169 }
1170
1171 die( 1 );
1172 }
1173
1177 public function finalSetup() {
1181
1182 # Turn off output buffering again, it might have been turned on in the settings files
1183 if ( ob_get_level() ) {
1184 ob_end_flush();
1185 }
1186 # Same with these
1187 $wgCommandLineMode = true;
1188
1189 # Override $wgServer
1190 if ( $this->hasOption( 'server' ) ) {
1191 $wgServer = $this->getOption( 'server', $wgServer );
1192 }
1193
1194 # If these were passed, use them
1195 if ( $this->mDbUser ) {
1197 }
1198 if ( $this->mDbPass ) {
1200 }
1201 if ( $this->hasOption( 'dbgroupdefault' ) ) {
1202 $wgDBDefaultGroup = $this->getOption( 'dbgroupdefault', null );
1203
1204 MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
1205 }
1206
1207 if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) {
1210
1211 if ( $wgDBservers ) {
1215 foreach ( $wgDBservers as $i => $server ) {
1216 $wgDBservers[$i]['user'] = $wgDBuser;
1217 $wgDBservers[$i]['password'] = $wgDBpassword;
1218 }
1219 }
1220 if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
1221 $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
1222 $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
1223 }
1224 MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
1225 }
1226
1227 # Apply debug settings
1228 if ( $this->hasOption( 'mwdebug' ) ) {
1229 require __DIR__ . '/../includes/DevelopmentSettings.php';
1230 }
1231
1232 // Per-script profiling; useful for debugging
1233 $this->activateProfiler();
1234
1235 $this->afterFinalSetup();
1236
1237 $wgShowExceptionDetails = true;
1238 $wgShowHostnames = true;
1239
1240 Wikimedia\suppressWarnings();
1241 set_time_limit( 0 );
1242 Wikimedia\restoreWarnings();
1243
1244 $this->adjustMemoryLimit();
1245 }
1246
1250 protected function afterFinalSetup() {
1251 if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
1252 // @phan-suppress-next-line PhanUndeclaredConstant
1253 call_user_func( MW_CMDLINE_CALLBACK );
1254 }
1255 }
1256
1261 public function globals() {
1262 if ( $this->hasOption( 'globals' ) ) {
1263 print_r( $GLOBALS );
1264 }
1265 }
1266
1271 public function loadSettings() {
1272 global $wgCommandLineMode, $IP;
1273
1274 if ( isset( $this->mOptions['conf'] ) ) {
1275 $settingsFile = $this->mOptions['conf'];
1276 } elseif ( defined( "MW_CONFIG_FILE" ) ) {
1277 $settingsFile = MW_CONFIG_FILE;
1278 } else {
1279 $settingsFile = "$IP/LocalSettings.php";
1280 }
1281 if ( isset( $this->mOptions['wiki'] ) ) {
1282 $bits = explode( '-', $this->mOptions['wiki'], 2 );
1283 define( 'MW_DB', $bits[0] );
1284 define( 'MW_PREFIX', $bits[1] ?? '' );
1285 } elseif ( isset( $this->mOptions['server'] ) ) {
1286 // Provide the option for site admins to detect and configure
1287 // multiple wikis based on server names. This offers --server
1288 // as alternative to --wiki.
1289 // See https://www.mediawiki.org/wiki/Manual:Wiki_family
1290 $_SERVER['SERVER_NAME'] = $this->mOptions['server'];
1291 }
1292
1293 if ( !is_readable( $settingsFile ) ) {
1294 $this->fatalError( "A copy of your installation's LocalSettings.php\n" .
1295 "must exist and be readable in the source directory.\n" .
1296 "Use --conf to specify it." );
1297 }
1298 $wgCommandLineMode = true;
1299
1300 return $settingsFile;
1301 }
1302
1308 public function purgeRedundantText( $delete = true ) {
1310
1311 # Data should come off the master, wrapped in a transaction
1312 $dbw = $this->getDB( DB_MASTER );
1313 $this->beginTransaction( $dbw, __METHOD__ );
1314
1316 # Get "active" text records from the revisions table
1317 $cur = [];
1318 $this->output( 'Searching for active text records in revisions table...' );
1319 $res = $dbw->select( 'revision', 'rev_text_id', [], __METHOD__, [ 'DISTINCT' ] );
1320 foreach ( $res as $row ) {
1321 $cur[] = $row->rev_text_id;
1322 }
1323 $this->output( "done.\n" );
1324
1325 # Get "active" text records from the archive table
1326 $this->output( 'Searching for active text records in archive table...' );
1327 $res = $dbw->select( 'archive', 'ar_text_id', [], __METHOD__, [ 'DISTINCT' ] );
1328 foreach ( $res as $row ) {
1329 # old pre-MW 1.5 records can have null ar_text_id's.
1330 if ( $row->ar_text_id !== null ) {
1331 $cur[] = $row->ar_text_id;
1332 }
1333 }
1334 $this->output( "done.\n" );
1335 } else {
1336 # Get "active" text records via the content table
1337 $cur = [];
1338 $this->output( 'Searching for active text records via contents table...' );
1339 $res = $dbw->select( 'content', 'content_address', [], __METHOD__, [ 'DISTINCT' ] );
1340 $blobStore = MediaWikiServices::getInstance()->getBlobStore();
1341 foreach ( $res as $row ) {
1342 // @phan-suppress-next-line PhanUndeclaredMethod
1343 $textId = $blobStore->getTextIdFromAddress( $row->content_address );
1344 if ( $textId ) {
1345 $cur[] = $textId;
1346 }
1347 }
1348 $this->output( "done.\n" );
1349 }
1350 $this->output( "done.\n" );
1351
1352 # Get the IDs of all text records not in these sets
1353 $this->output( 'Searching for inactive text records...' );
1354 $cond = 'old_id NOT IN ( ' . $dbw->makeList( $cur ) . ' )';
1355 $res = $dbw->select( 'text', 'old_id', [ $cond ], __METHOD__, [ 'DISTINCT' ] );
1356 $old = [];
1357 foreach ( $res as $row ) {
1358 $old[] = $row->old_id;
1359 }
1360 $this->output( "done.\n" );
1361
1362 # Inform the user of what we're going to do
1363 $count = count( $old );
1364 $this->output( "$count inactive items found.\n" );
1365
1366 # Delete as appropriate
1367 if ( $delete && $count ) {
1368 $this->output( 'Deleting...' );
1369 $dbw->delete( 'text', [ 'old_id' => $old ], __METHOD__ );
1370 $this->output( "done.\n" );
1371 }
1372
1373 $this->commitTransaction( $dbw, __METHOD__ );
1374 }
1375
1380 protected function getDir() {
1381 return __DIR__;
1382 }
1383
1396 protected function getDB( $db, $groups = [], $dbDomain = false ) {
1397 if ( $this->mDb === null ) {
1398 return MediaWikiServices::getInstance()
1399 ->getDBLoadBalancerFactory()
1400 ->getMainLB( $dbDomain )
1401 ->getMaintenanceConnectionRef( $db, $groups, $dbDomain );
1402 }
1403
1404 return $this->mDb;
1405 }
1406
1412 public function setDB( IMaintainableDatabase $db ) {
1413 $this->mDb = $db;
1414 }
1415
1426 protected function beginTransaction( IDatabase $dbw, $fname ) {
1427 $dbw->begin( $fname );
1428 }
1429
1441 protected function commitTransaction( IDatabase $dbw, $fname ) {
1442 $dbw->commit( $fname );
1443 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
1444 $waitSucceeded = $lbFactory->waitForReplication(
1445 [ 'timeout' => 30, 'ifWritesSince' => $this->lastReplicationWait ]
1446 );
1447 $this->lastReplicationWait = microtime( true );
1448 return $waitSucceeded;
1449 }
1450
1461 protected function rollbackTransaction( IDatabase $dbw, $fname ) {
1462 $dbw->rollback( $fname );
1463 }
1464
1469 private function lockSearchindex( $db ) {
1470 $write = [ 'searchindex' ];
1471 $read = [
1472 'page',
1473 'revision',
1474 'text',
1475 'interwiki',
1476 'l10n_cache',
1477 'user',
1478 'page_restrictions'
1479 ];
1480 $db->lockTables( $read, $write, __CLASS__ . '-searchIndexLock' );
1481 }
1482
1487 private function unlockSearchindex( $db ) {
1488 $db->unlockTables( __CLASS__ . '-searchIndexLock' );
1489 }
1490
1496 private function relockSearchindex( $db ) {
1497 $this->unlockSearchindex( $db );
1498 $this->lockSearchindex( $db );
1499 }
1500
1508 public function updateSearchIndex( $maxLockTime, $callback, $dbw, $results ) {
1509 $lockTime = time();
1510
1511 # Lock searchindex
1512 if ( $maxLockTime ) {
1513 $this->output( " --- Waiting for lock ---" );
1514 $this->lockSearchindex( $dbw );
1515 $lockTime = time();
1516 $this->output( "\n" );
1517 }
1518
1519 # Loop through the results and do a search update
1520 foreach ( $results as $row ) {
1521 # Allow reads to be processed
1522 if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
1523 $this->output( " --- Relocking ---" );
1524 $this->relockSearchindex( $dbw );
1525 $lockTime = time();
1526 $this->output( "\n" );
1527 }
1528 call_user_func( $callback, $dbw, $row );
1529 }
1530
1531 # Unlock searchindex
1532 if ( $maxLockTime ) {
1533 $this->output( " --- Unlocking --" );
1534 $this->unlockSearchindex( $dbw );
1535 $this->output( "\n" );
1536 }
1537 }
1538
1545 public function updateSearchIndexForPage( $dbw, $pageId ) {
1546 // Get current revision
1547 $rev = Revision::loadFromPageId( $dbw, $pageId );
1548 $title = null;
1549 if ( $rev ) {
1550 $titleObj = $rev->getTitle();
1551 $title = $titleObj->getPrefixedDBkey();
1552 $this->output( "$title..." );
1553 # Update searchindex
1554 $u = new SearchUpdate( $pageId, $titleObj, $rev->getContent() );
1555 $u->doUpdate();
1556 $this->output( "\n" );
1557 }
1558
1559 return $title;
1560 }
1561
1572 protected function countDown( $seconds ) {
1573 if ( $this->isQuiet() ) {
1574 return;
1575 }
1576 for ( $i = $seconds; $i >= 0; $i-- ) {
1577 if ( $i != $seconds ) {
1578 $this->output( str_repeat( "\x08", strlen( $i + 1 ) ) );
1579 }
1580 $this->output( $i );
1581 if ( $i ) {
1582 sleep( 1 );
1583 }
1584 }
1585 $this->output( "\n" );
1586 }
1587
1596 public static function posix_isatty( $fd ) {
1597 if ( !function_exists( 'posix_isatty' ) ) {
1598 return !$fd;
1599 } else {
1600 return posix_isatty( $fd );
1601 }
1602 }
1603
1609 public static function readconsole( $prompt = '> ' ) {
1610 static $isatty = null;
1611 if ( is_null( $isatty ) ) {
1612 $isatty = self::posix_isatty( 0 /*STDIN*/ );
1613 }
1614
1615 if ( $isatty && function_exists( 'readline' ) ) {
1616 return readline( $prompt );
1617 } else {
1618 if ( $isatty ) {
1619 $st = self::readlineEmulation( $prompt );
1620 } else {
1621 if ( feof( STDIN ) ) {
1622 $st = false;
1623 } else {
1624 $st = fgets( STDIN, 1024 );
1625 }
1626 }
1627 if ( $st === false ) {
1628 return false;
1629 }
1630 $resp = trim( $st );
1631
1632 return $resp;
1633 }
1634 }
1635
1641 private static function readlineEmulation( $prompt ) {
1642 $bash = ExecutableFinder::findInDefaultPaths( 'bash' );
1643 if ( !wfIsWindows() && $bash ) {
1644 $retval = false;
1645 $encPrompt = Shell::escape( $prompt );
1646 $command = "read -er -p $encPrompt && echo \"\$REPLY\"";
1647 $encCommand = Shell::escape( $command );
1648 $line = Shell::escape( "$bash -c $encCommand", $retval, [], [ 'walltime' => 0 ] );
1649
1650 if ( $retval == 0 ) {
1651 return $line;
1652 } elseif ( $retval == 127 ) {
1653 // Couldn't execute bash even though we thought we saw it.
1654 // Shell probably spit out an error message, sorry :(
1655 // Fall through to fgets()...
1656 } else {
1657 // EOF/ctrl+D
1658 return false;
1659 }
1660 }
1661
1662 // Fallback... we'll have no editing controls, EWWW
1663 if ( feof( STDIN ) ) {
1664 return false;
1665 }
1666 print $prompt;
1667
1668 return fgets( STDIN, 1024 );
1669 }
1670
1678 public static function getTermSize() {
1679 $default = [ 80, 50 ];
1680 if ( wfIsWindows() ) {
1681 return $default;
1682 }
1683 if ( Shell::isDisabled() ) {
1684 return $default;
1685 }
1686 // It's possible to get the screen size with VT-100 terminal escapes,
1687 // but reading the responses is not possible without setting raw mode
1688 // (unless you want to require the user to press enter), and that
1689 // requires an ioctl(), which we can't do. So we have to shell out to
1690 // something that can do the relevant syscalls. There are a few
1691 // options. Linux and Mac OS X both have "stty size" which does the
1692 // job directly.
1693 $result = Shell::command( 'stty', 'size' )
1694 ->execute();
1695 if ( $result->getExitCode() !== 0 ) {
1696 return $default;
1697 }
1698 if ( !preg_match( '/^(\d+) (\d+)$/', $result->getStdout(), $m ) ) {
1699 return $default;
1700 }
1701 return [ intval( $m[2] ), intval( $m[1] ) ];
1702 }
1703
1708 public static function requireTestsAutoloader() {
1709 require_once __DIR__ . '/../tests/common/TestsAutoLoader.php';
1710 }
1711
1722 protected function parseIntList( $text ) {
1723 $ids = preg_split( '/[\s,;:|]+/', $text );
1724 $ids = array_map(
1725 function ( $id ) {
1726 return (int)$id;
1727 },
1728 $ids
1729 );
1730 return array_filter( $ids );
1731 }
1732}
1733
1738 protected $mSelf = "FakeMaintenanceScript";
1739
1740 public function execute() {
1741 }
1742}
1743
1748abstract class LoggedUpdateMaintenance extends Maintenance {
1749 public function __construct() {
1750 parent::__construct();
1751 $this->addOption( 'force', 'Run the update even if it was completed already' );
1752 $this->setBatchSize( 200 );
1753 }
1754
1755 public function execute() {
1756 $db = $this->getDB( DB_MASTER );
1757 $key = $this->getUpdateKey();
1758
1759 if ( !$this->hasOption( 'force' )
1760 && $db->selectRow( 'updatelog', '1', [ 'ul_key' => $key ], __METHOD__ )
1761 ) {
1762 $this->output( "..." . $this->updateSkippedMessage() . "\n" );
1763
1764 return true;
1765 }
1766
1767 if ( !$this->doDBUpdates() ) {
1768 return false;
1769 }
1770
1771 $db->insert( 'updatelog', [ 'ul_key' => $key ], __METHOD__, [ 'IGNORE' ] );
1772
1773 return true;
1774 }
1775
1780 protected function updateSkippedMessage() {
1781 $key = $this->getUpdateKey();
1782
1783 return "Update '{$key}' already logged as completed. Use --force to run it again.";
1784 }
1785
1791 abstract protected function doDBUpdates();
1792
1797 abstract protected function getUpdateKey();
1798}
getDB()
$GLOBALS['IP']
$wgDBuser
Database username.
int $wgMultiContentRevisionSchemaMigrationStage
RevisionStore table schema migration stage (content, slots, content_models & slot_roles tables).
$wgDBadminuser
Separate username for maintenance tasks.
$wgTrxProfilerLimits
Performance expectations for DB usage.
$wgDBservers
Database load balancer This is a two-dimensional array, an array of server info structures Fields are...
$wgShowHostnames
Expose backend server host names through the API and various HTML comments.
$wgProfileLimit
Only record profiling info for pages that took longer than this.
$wgDBDefaultGroup
Default group to use when getting database connections.
$wgDBadminpassword
Separate password for maintenance tasks.
$wgProfiler
Profiler configuration.
$wgShowExceptionDetails
If set to true, uncaught exceptions will print the exception message and a complete stack trace to ou...
$wgServer
URL of the server.
$wgLBFactoryConf
Load balancer factory configuration To set up a multi-master wiki farm, set the class here to somethi...
$wgDBpassword
Database user's password.
global $wgCommandLineMode
wfHostname()
Get host name of the current machine, for use in error reporting.
wfIsWindows()
Check if the operating system is Windows.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$maintClass
const RUN_MAINTENANCE_IF_MAIN
wfEntryPointCheck( $format='text', $scriptPath='/')
Check PHP version and that external dependencies are installed, and display an informative error if e...
$IP
Definition WebStart.php:41
$line
Definition cdb.php:59
$command
Definition cdb.php:65
if( $line===false) $args
Definition cdb.php:64
Fake maintenance wrapper, mostly used for the web installer/updater.
execute()
Do the actual work.
Class for scripts that perform database maintenance and want to log the update in updatelog so we can...
__construct()
Default constructor.
doDBUpdates()
Do the actual work.
updateSkippedMessage()
Message to show that the update was done already and was just skipped.
getUpdateKey()
Get the update key name to go in the update log table.
execute()
Do the actual work.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.
setup()
Do some sanity checking and basic setup.
array[] $mParams
Array of desired/allowed params.
__construct()
Default constructor.
error( $err, $die=0)
Throw an error to the user.
const STDIN_ALL
getName()
Get the script's name.
array[] $mGenericParameters
Generic options added by addDefaultParams()
addArg( $arg, $description, $required=true)
Add some args that are needed.
requireExtension( $name)
Indicate that the specified extension must be loaded before the script can run.
relockSearchindex( $db)
Unlock and lock again Since the lock is low-priority, queued reads will be able to complete.
int $mBatchSize
Batch size.
setAgentAndTriggers()
Set triggers like when to try to run deferred updates.
setAllowUnregisteredOptions( $allow)
Sets whether to allow unregistered options, which are options passed to a script that do not match an...
beginTransaction(IDatabase $dbw, $fname)
Begin a transcation on a DB.
static getTermSize()
Get the terminal size as a two-element array where the first element is the width (number of columns)...
clearParamsAndArgs()
Clear all params and arguments.
static setLBFactoryTriggers(LBFactory $LBFactory, Config $config)
array $requiredExtensions
setParam(&$options, $option, $value)
Helper function used solely by loadParamsAndArgs to prevent code duplication.
const DB_NONE
Constants for DB access type.
commitTransaction(IDatabase $dbw, $fname)
Commit the transcation on a DB handle and wait for replica DBs to catch up.
array[] $mDependantParameters
Generic options which might or not be supported by the script.
output( $out, $channel=null)
Throw some output to the user.
supportsOption( $name)
Checks to see if a particular option in supported.
getStdin( $len=null)
Return input from stdin.
cleanupChanneled()
Clean up channeled output.
memoryLimit()
Normally we disable the memory_limit when running admin scripts.
afterFinalSetup()
Execute a callback function at the end of initialisation.
hasArg( $argId=0)
Does a given argument exist?
getDir()
Get the maintenance directory.
addDefaultParams()
Add the default parameters to the scripts.
deleteOption( $name)
Remove an option.
static readlineEmulation( $prompt)
Emulate readline()
static requireTestsAutoloader()
Call this to set up the autoloader to allow classes to be used from the tests directory.
loadParamsAndArgs( $self=null, $opts=null, $args=null)
Process command line arguments $mOptions becomes an array with keys set to the option names $mArgs be...
outputChanneled( $msg, $channel=null)
Message outputter with channeled message support.
finalSetup()
Handle some last-minute setup here.
loadSpecialVars()
Handle the special variables that are global to all scripts.
setDB(IMaintainableDatabase $db)
Sets database object to be returned by getDB().
float $lastReplicationWait
UNIX timestamp.
array $orderedOptions
Used to read the options in the order they were passed.
loadSettings()
Generic setup for most installs.
hasOption( $name)
Checks to see if a particular option exists.
purgeRedundantText( $delete=true)
Support function for cleaning up redundant text records.
countDown( $seconds)
Count down from $seconds to zero on the terminal, with a one-second pause between showing each number...
runChild( $maintClass, $classFile=null)
Run a child maintenance script.
IMaintainableDatabase $mDb
Used by getDB() / setDB()
execute()
Do the actual work.
static readconsole( $prompt='> ')
Prompt the console for input.
static posix_isatty( $fd)
Wrapper for posix_isatty() We default as considering stdin a tty (for nice readline methods) but trea...
adjustMemoryLimit()
Adjusts PHP's memory limit to better suit our needs, if needed.
validateParamsAndArgs()
Run some validation checks on the params, etc.
getDbType()
Does the script need different DB access? By default, we give Maintenance scripts normal rights to th...
getBatchSize()
Returns batch size.
unlockSearchindex( $db)
Unlock the tables.
parseIntList( $text)
Utility function to parse a string (perhaps from a command line option) into a list of integers (perh...
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
activateProfiler()
Activate the profiler (assuming $wgProfiler is set)
maybeHelp( $force=false)
Maybe show the help.
updateSearchIndexForPage( $dbw, $pageId)
Update the searchindex table for a given pageid.
resource $fileHandle
Used when creating separate schema files.
loadWithArgv( $argv)
Load params and arguments from a given array of command-line arguments.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
static shouldExecute()
Should we execute the maintenance script, or just allow it to be included as a standalone class?...
getOption( $name, $default=null)
Get an option, or return the default.
Config $config
Accessible via getConfig()
lockSearchindex( $db)
Lock the search index.
checkRequiredExtensions()
Verify that the required extensions are installed.
rollbackTransaction(IDatabase $dbw, $fname)
Rollback the transcation on a DB handle.
updateSearchIndex( $maxLockTime, $callback, $dbw, $results)
Perform a search index update with locking.
globals()
Potentially debug globals.
setConfig(Config $config)
setBatchSize( $s=0)
Set the batch size.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
PSR-3 logger instance factory.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Executes shell commands.
Definition Shell.php:44
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:261
Database independant search index updater.
An interface for generating database load balancers.
Definition LBFactory.php:40
while(( $__line=Maintenance::readconsole()) !==false) print
Definition eval.php:64
const SCHEMA_COMPAT_READ_OLD
Definition Defines.php:274
Interface for configuration instances.
Definition Config.php:28
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
rollback( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Rollback a transaction previously started using begin()
commit( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Commits a transaction previously started using begin()
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
Advanced database interface for IDatabase handles that include maintenance methods.
Result wrapper for grabbing data queried from an IDatabase object.
const DB_MASTER
Definition defines.php:26