MediaWiki  master
Maintenance.php
Go to the documentation of this file.
1 <?php
23 // Bail on old versions of PHP, or if composer has not been run yet to install
24 // dependencies.
25 require_once __DIR__ . '/../includes/PHPVersionCheck.php';
26 wfEntryPointCheck( 'text' );
27 
29 
35 // Define this so scripts can easily find doMaintenance.php
36 define( 'RUN_MAINTENANCE_IF_MAIN', __DIR__ . '/doMaintenance.php' );
37 
41 define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
42 
43 $maintClass = false;
44 
50 
60 abstract class Maintenance {
65  const DB_NONE = 0;
66  const DB_STD = 1;
67  const DB_ADMIN = 2;
68 
69  // Const for getStdin()
70  const STDIN_ALL = 'all';
71 
72  // This is the desired params
73  protected $mParams = [];
74 
75  // Array of mapping short parameters to long ones
76  protected $mShortParamsMap = [];
77 
78  // Array of desired args
79  protected $mArgList = [];
80 
81  // This is the list of options that were actually passed
82  protected $mOptions = [];
83 
84  // This is the list of arguments that were actually passed
85  protected $mArgs = [];
86 
87  // Allow arbitrary options to be passed, or only specified ones?
88  protected $mAllowUnregisteredOptions = false;
89 
90  // Name of the script currently running
91  protected $mSelf;
92 
93  // Special vars for params that are always used
94  protected $mQuiet = false;
95  protected $mDbUser, $mDbPass;
96 
97  // A description of the script, children should change this via addDescription()
98  protected $mDescription = '';
99 
100  // Have we already loaded our user input?
101  protected $mInputLoaded = false;
102 
109  protected $mBatchSize = null;
110 
111  // Generic options added by addDefaultParams()
112  private $mGenericParameters = [];
113  // Generic options which might or not be supported by the script
114  private $mDependantParameters = [];
115 
120  private $mDb = null;
121 
123  private $lastReplicationWait = 0.0;
124 
129  public $fileHandle;
130 
136  private $config;
137 
142  private $requiredExtensions = [];
143 
155  public $orderedOptions = [];
156 
161  public function __construct() {
162  // Setup $IP, using MW_INSTALL_PATH if it exists
163  global $IP;
164  $IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
165  ? getenv( 'MW_INSTALL_PATH' )
166  : realpath( __DIR__ . '/..' );
167 
168  $this->addDefaultParams();
169  register_shutdown_function( [ $this, 'outputChanneled' ], false );
170  }
171 
179  public static function shouldExecute() {
180  global $wgCommandLineMode;
181 
182  if ( !function_exists( 'debug_backtrace' ) ) {
183  // If someone has a better idea...
184  return $wgCommandLineMode;
185  }
186 
187  $bt = debug_backtrace();
188  $count = count( $bt );
189  if ( $count < 2 ) {
190  return false; // sanity
191  }
192  if ( $bt[0]['class'] !== self::class || $bt[0]['function'] !== 'shouldExecute' ) {
193  return false; // last call should be to this function
194  }
195  $includeFuncs = [ 'require_once', 'require', 'include', 'include_once' ];
196  for ( $i = 1; $i < $count; $i++ ) {
197  if ( !in_array( $bt[$i]['function'], $includeFuncs ) ) {
198  return false; // previous calls should all be "requires"
199  }
200  }
201 
202  return true;
203  }
204 
213  abstract public function execute();
214 
221  protected function supportsOption( $name ) {
222  return isset( $this->mParams[$name] );
223  }
224 
236  protected function addOption( $name, $description, $required = false,
237  $withArg = false, $shortName = false, $multiOccurrence = false
238  ) {
239  $this->mParams[$name] = [
240  'desc' => $description,
241  'require' => $required,
242  'withArg' => $withArg,
243  'shortName' => $shortName,
244  'multiOccurrence' => $multiOccurrence
245  ];
246 
247  if ( $shortName !== false ) {
248  $this->mShortParamsMap[$shortName] = $name;
249  }
250  }
251 
257  protected function hasOption( $name ) {
258  return isset( $this->mOptions[$name] );
259  }
260 
271  protected function getOption( $name, $default = null ) {
272  if ( $this->hasOption( $name ) ) {
273  return $this->mOptions[$name];
274  } else {
275  // Set it so we don't have to provide the default again
276  $this->mOptions[$name] = $default;
277 
278  return $this->mOptions[$name];
279  }
280  }
281 
288  protected function addArg( $arg, $description, $required = true ) {
289  $this->mArgList[] = [
290  'name' => $arg,
291  'desc' => $description,
292  'require' => $required
293  ];
294  }
295 
300  protected function deleteOption( $name ) {
301  unset( $this->mParams[$name] );
302  }
303 
309  protected function setAllowUnregisteredOptions( $allow ) {
310  $this->mAllowUnregisteredOptions = $allow;
311  }
312 
317  protected function addDescription( $text ) {
318  $this->mDescription = $text;
319  }
320 
326  protected function hasArg( $argId = 0 ) {
327  return isset( $this->mArgs[$argId] );
328  }
329 
336  protected function getArg( $argId = 0, $default = null ) {
337  return $this->hasArg( $argId ) ? $this->mArgs[$argId] : $default;
338  }
339 
347  protected function getBatchSize() {
348  return $this->mBatchSize;
349  }
350 
355  protected function setBatchSize( $s = 0 ) {
356  $this->mBatchSize = $s;
357 
358  // If we support $mBatchSize, show the option.
359  // Used to be in addDefaultParams, but in order for that to
360  // work, subclasses would have to call this function in the constructor
361  // before they called parent::__construct which is just weird
362  // (and really wasn't done).
363  if ( $this->mBatchSize ) {
364  $this->addOption( 'batch-size', 'Run this many operations ' .
365  'per batch, default: ' . $this->mBatchSize, false, true );
366  if ( isset( $this->mParams['batch-size'] ) ) {
367  // This seems a little ugly...
368  $this->mDependantParameters['batch-size'] = $this->mParams['batch-size'];
369  }
370  }
371  }
372 
377  public function getName() {
378  return $this->mSelf;
379  }
380 
387  protected function getStdin( $len = null ) {
388  if ( $len == self::STDIN_ALL ) {
389  return file_get_contents( 'php://stdin' );
390  }
391  $f = fopen( 'php://stdin', 'rt' );
392  if ( !$len ) {
393  return $f;
394  }
395  $input = fgets( $f, $len );
396  fclose( $f );
397 
398  return rtrim( $input );
399  }
400 
404  public function isQuiet() {
405  return $this->mQuiet;
406  }
407 
414  protected function output( $out, $channel = null ) {
415  // This is sometimes called very early, before Setup.php is included.
416  if ( class_exists( MediaWikiServices::class ) ) {
417  // Try to periodically flush buffered metrics to avoid OOMs
418  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
419  if ( $stats->getDataCount() > 1000 ) {
420  MediaWiki::emitBufferedStatsdData( $stats, $this->getConfig() );
421  }
422  }
423 
424  if ( $this->mQuiet ) {
425  return;
426  }
427  if ( $channel === null ) {
428  $this->cleanupChanneled();
429  print $out;
430  } else {
431  $out = preg_replace( '/\n\z/', '', $out );
432  $this->outputChanneled( $out, $channel );
433  }
434  }
435 
442  protected function error( $err, $die = 0 ) {
443  if ( intval( $die ) !== 0 ) {
444  wfDeprecated( __METHOD__ . '( $err, $die )', '1.31' );
445  $this->fatalError( $err, intval( $die ) );
446  }
447  $this->outputChanneled( false );
448  if (
449  ( PHP_SAPI == 'cli' || PHP_SAPI == 'phpdbg' ) &&
450  !defined( 'MW_PHPUNIT_TEST' )
451  ) {
452  fwrite( STDERR, $err . "\n" );
453  } else {
454  print $err;
455  }
456  }
457 
465  protected function fatalError( $msg, $exitCode = 1 ) {
466  $this->error( $msg );
467  exit( $exitCode );
468  }
469 
470  private $atLineStart = true;
471  private $lastChannel = null;
472 
476  public function cleanupChanneled() {
477  if ( !$this->atLineStart ) {
478  print "\n";
479  $this->atLineStart = true;
480  }
481  }
482 
491  public function outputChanneled( $msg, $channel = null ) {
492  if ( $msg === false ) {
493  $this->cleanupChanneled();
494 
495  return;
496  }
497 
498  // End the current line if necessary
499  if ( !$this->atLineStart && $channel !== $this->lastChannel ) {
500  print "\n";
501  }
502 
503  print $msg;
504 
505  $this->atLineStart = false;
506  if ( $channel === null ) {
507  // For unchanneled messages, output trailing newline immediately
508  print "\n";
509  $this->atLineStart = true;
510  }
511  $this->lastChannel = $channel;
512  }
513 
524  public function getDbType() {
525  return self::DB_STD;
526  }
527 
531  protected function addDefaultParams() {
532  # Generic (non script dependant) options:
533 
534  $this->addOption( 'help', 'Display this help message', false, false, 'h' );
535  $this->addOption( 'quiet', 'Whether to suppress non-error output', false, false, 'q' );
536  $this->addOption( 'conf', 'Location of LocalSettings.php, if not default', false, true );
537  $this->addOption( 'wiki', 'For specifying the wiki ID', false, true );
538  $this->addOption( 'globals', 'Output globals at the end of processing for debugging' );
539  $this->addOption(
540  'memory-limit',
541  'Set a specific memory limit for the script, '
542  . '"max" for no limit or "default" to avoid changing it',
543  false,
544  true
545  );
546  $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " .
547  "http://en.wikipedia.org. This is sometimes necessary because " .
548  "server name detection may fail in command line scripts.", false, true );
549  $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true );
550  // This is named --mwdebug, because --debug would conflict in the phpunit.php CLI script.
551  $this->addOption( 'mwdebug', 'Enable built-in MediaWiki development settings', false, true );
552 
553  # Save generic options to display them separately in help
554  $this->mGenericParameters = $this->mParams;
555 
556  # Script dependant options:
557 
558  // If we support a DB, show the options
559  if ( $this->getDbType() > 0 ) {
560  $this->addOption( 'dbuser', 'The DB user to use for this script', false, true );
561  $this->addOption( 'dbpass', 'The password to use for this script', false, true );
562  $this->addOption( 'dbgroupdefault', 'The default DB group to use.', false, true );
563  }
564 
565  # Save additional script dependant options to display
566  # ┬áthem separately in help
567  $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters );
568  }
569 
574  public function getConfig() {
575  if ( $this->config === null ) {
576  $this->config = MediaWikiServices::getInstance()->getMainConfig();
577  }
578 
579  return $this->config;
580  }
581 
586  public function setConfig( Config $config ) {
587  $this->config = $config;
588  }
589 
599  protected function requireExtension( $name ) {
600  $this->requiredExtensions[] = $name;
601  }
602 
608  public function checkRequiredExtensions() {
609  $registry = ExtensionRegistry::getInstance();
610  $missing = [];
611  foreach ( $this->requiredExtensions as $name ) {
612  if ( !$registry->isLoaded( $name ) ) {
613  $missing[] = $name;
614  }
615  }
616 
617  if ( $missing ) {
618  $joined = implode( ', ', $missing );
619  $msg = "The following extensions are required to be installed "
620  . "for this script to run: $joined. Please enable them and then try again.";
621  $this->fatalError( $msg );
622  }
623  }
624 
629  public function setAgentAndTriggers() {
630  if ( function_exists( 'posix_getpwuid' ) ) {
631  $agent = posix_getpwuid( posix_geteuid() )['name'];
632  } else {
633  $agent = 'sysadmin';
634  }
635  $agent .= '@' . wfHostname();
636 
637  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
638  // Add a comment for easy SHOW PROCESSLIST interpretation
639  $lbFactory->setAgentName(
640  mb_strlen( $agent ) > 15 ? mb_substr( $agent, 0, 15 ) . '...' : $agent
641  );
642  self::setLBFactoryTriggers( $lbFactory, $this->getConfig() );
643  }
644 
650  public static function setLBFactoryTriggers( LBFactory $LBFactory, Config $config ) {
651  $services = MediaWikiServices::getInstance();
652  $stats = $services->getStatsdDataFactory();
653  // Hook into period lag checks which often happen in long-running scripts
654  $lbFactory = $services->getDBLoadBalancerFactory();
655  $lbFactory->setWaitForReplicationListener(
656  __METHOD__,
657  function () use ( $stats, $config ) {
658  // Check config in case of JobRunner and unit tests
659  if ( $config->get( 'CommandLineMode' ) ) {
661  }
662  // Try to periodically flush buffered metrics to avoid OOMs
663  MediaWiki::emitBufferedStatsdData( $stats, $config );
664  }
665  );
666  // Check for other windows to run them. A script may read or do a few writes
667  // to the master but mostly be writing to something else, like a file store.
668  $lbFactory->getMainLB()->setTransactionListener(
669  __METHOD__,
670  function ( $trigger ) use ( $stats, $config ) {
671  // Check config in case of JobRunner and unit tests
672  if ( $config->get( 'CommandLineMode' ) && $trigger === IDatabase::TRIGGER_COMMIT ) {
674  }
675  // Try to periodically flush buffered metrics to avoid OOMs
676  MediaWiki::emitBufferedStatsdData( $stats, $config );
677  }
678  );
679  }
680 
688  public function runChild( $maintClass, $classFile = null ) {
689  // Make sure the class is loaded first
690  if ( !class_exists( $maintClass ) ) {
691  if ( $classFile ) {
692  require_once $classFile;
693  }
694  if ( !class_exists( $maintClass ) ) {
695  $this->error( "Cannot spawn child: $maintClass" );
696  }
697  }
698 
702  $child = new $maintClass();
703  $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
704  if ( !is_null( $this->mDb ) ) {
705  $child->setDB( $this->mDb );
706  }
707 
708  return $child;
709  }
710 
714  public function setup() {
715  global $IP, $wgCommandLineMode;
716 
717  # Abort if called from a web server
718  # wfIsCLI() is not available yet
719  if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
720  $this->fatalError( 'This script must be run from the command line' );
721  }
722 
723  if ( $IP === null ) {
724  $this->fatalError( "\$IP not set, aborting!\n" .
725  '(Did you forget to call parent::__construct() in your maintenance script?)' );
726  }
727 
728  # Make sure we can handle script parameters
729  if ( !defined( 'HPHP_VERSION' ) && !ini_get( 'register_argc_argv' ) ) {
730  $this->fatalError( 'Cannot get command line arguments, register_argc_argv is set to false' );
731  }
732 
733  // Send PHP warnings and errors to stderr instead of stdout.
734  // This aids in diagnosing problems, while keeping messages
735  // out of redirected output.
736  if ( ini_get( 'display_errors' ) ) {
737  ini_set( 'display_errors', 'stderr' );
738  }
739 
740  $this->loadParamsAndArgs();
741  $this->maybeHelp();
742 
743  # Set the memory limit
744  # Note we need to set it again later in cache LocalSettings changed it
745  $this->adjustMemoryLimit();
746 
747  # Set max execution time to 0 (no limit). PHP.net says that
748  # "When running PHP from the command line the default setting is 0."
749  # But sometimes this doesn't seem to be the case.
750  ini_set( 'max_execution_time', 0 );
751 
752  # Define us as being in MediaWiki
753  define( 'MEDIAWIKI', true );
754 
755  $wgCommandLineMode = true;
756 
757  # Turn off output buffering if it's on
758  while ( ob_get_level() > 0 ) {
759  ob_end_flush();
760  }
761 
762  $this->validateParamsAndArgs();
763  }
764 
774  public function memoryLimit() {
775  $limit = $this->getOption( 'memory-limit', 'max' );
776  $limit = trim( $limit, "\" '" ); // trim quotes in case someone misunderstood
777  return $limit;
778  }
779 
783  protected function adjustMemoryLimit() {
784  $limit = $this->memoryLimit();
785  if ( $limit == 'max' ) {
786  $limit = -1; // no memory limit
787  }
788  if ( $limit != 'default' ) {
789  ini_set( 'memory_limit', $limit );
790  }
791  }
792 
796  protected function activateProfiler() {
798 
799  $output = $this->getOption( 'profiler' );
800  if ( !$output ) {
801  return;
802  }
803 
804  if ( is_array( $wgProfiler ) && isset( $wgProfiler['class'] ) ) {
805  $class = $wgProfiler['class'];
807  $profiler = new $class(
808  [ 'sampling' => 1, 'output' => [ $output ] ]
809  + $wgProfiler
810  + [ 'threshold' => $wgProfileLimit ]
811  );
812  $profiler->setTemplated( true );
813  Profiler::replaceStubInstance( $profiler );
814  }
815 
816  $trxProfiler = Profiler::instance()->getTransactionProfiler();
817  $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
818  $trxProfiler->setExpectations( $wgTrxProfilerLimits['Maintenance'], __METHOD__ );
819  }
820 
824  public function clearParamsAndArgs() {
825  $this->mOptions = [];
826  $this->mArgs = [];
827  $this->mInputLoaded = false;
828  }
829 
837  public function loadWithArgv( $argv ) {
838  $options = [];
839  $args = [];
840  $this->orderedOptions = [];
841 
842  # Parse arguments
843  for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
844  if ( $arg == '--' ) {
845  # End of options, remainder should be considered arguments
846  $arg = next( $argv );
847  while ( $arg !== false ) {
848  $args[] = $arg;
849  $arg = next( $argv );
850  }
851  break;
852  } elseif ( substr( $arg, 0, 2 ) == '--' ) {
853  # Long options
854  $option = substr( $arg, 2 );
855  if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
856  $param = next( $argv );
857  if ( $param === false ) {
858  $this->error( "\nERROR: $option parameter needs a value after it\n" );
859  $this->maybeHelp( true );
860  }
861 
862  $this->setParam( $options, $option, $param );
863  } else {
864  $bits = explode( '=', $option, 2 );
865  if ( count( $bits ) > 1 ) {
866  $option = $bits[0];
867  $param = $bits[1];
868  } else {
869  $param = 1;
870  }
871 
872  $this->setParam( $options, $option, $param );
873  }
874  } elseif ( $arg == '-' ) {
875  # Lonely "-", often used to indicate stdin or stdout.
876  $args[] = $arg;
877  } elseif ( substr( $arg, 0, 1 ) == '-' ) {
878  # Short options
879  $argLength = strlen( $arg );
880  for ( $p = 1; $p < $argLength; $p++ ) {
881  $option = $arg[$p];
882  if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) {
883  $option = $this->mShortParamsMap[$option];
884  }
885 
886  if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
887  $param = next( $argv );
888  if ( $param === false ) {
889  $this->error( "\nERROR: $option parameter needs a value after it\n" );
890  $this->maybeHelp( true );
891  }
892  $this->setParam( $options, $option, $param );
893  } else {
894  $this->setParam( $options, $option, 1 );
895  }
896  }
897  } else {
898  $args[] = $arg;
899  }
900  }
901 
902  $this->mOptions = $options;
903  $this->mArgs = $args;
904  $this->loadSpecialVars();
905  $this->mInputLoaded = true;
906  }
907 
920  private function setParam( &$options, $option, $value ) {
921  $this->orderedOptions[] = [ $option, $value ];
922 
923  if ( isset( $this->mParams[$option] ) ) {
924  $multi = $this->mParams[$option]['multiOccurrence'];
925  } else {
926  $multi = false;
927  }
928  $exists = array_key_exists( $option, $options );
929  if ( $multi && $exists ) {
930  $options[$option][] = $value;
931  } elseif ( $multi ) {
932  $options[$option] = [ $value ];
933  } elseif ( !$exists ) {
934  $options[$option] = $value;
935  } else {
936  $this->error( "\nERROR: $option parameter given twice\n" );
937  $this->maybeHelp( true );
938  }
939  }
940 
950  public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
951  # If we were given opts or args, set those and return early
952  if ( $self ) {
953  $this->mSelf = $self;
954  $this->mInputLoaded = true;
955  }
956  if ( $opts ) {
957  $this->mOptions = $opts;
958  $this->mInputLoaded = true;
959  }
960  if ( $args ) {
961  $this->mArgs = $args;
962  $this->mInputLoaded = true;
963  }
964 
965  # If we've already loaded input (either by user values or from $argv)
966  # skip on loading it again. The array_shift() will corrupt values if
967  # it's run again and again
968  if ( $this->mInputLoaded ) {
969  $this->loadSpecialVars();
970 
971  return;
972  }
973 
974  global $argv;
975  $this->mSelf = $argv[0];
976  $this->loadWithArgv( array_slice( $argv, 1 ) );
977  }
978 
982  protected function validateParamsAndArgs() {
983  $die = false;
984  # Check to make sure we've got all the required options
985  foreach ( $this->mParams as $opt => $info ) {
986  if ( $info['require'] && !$this->hasOption( $opt ) ) {
987  $this->error( "Param $opt required!" );
988  $die = true;
989  }
990  }
991  # Check arg list too
992  foreach ( $this->mArgList as $k => $info ) {
993  if ( $info['require'] && !$this->hasArg( $k ) ) {
994  $this->error( 'Argument <' . $info['name'] . '> required!' );
995  $die = true;
996  }
997  }
998  if ( !$this->mAllowUnregisteredOptions ) {
999  # Check for unexpected options
1000  foreach ( $this->mOptions as $opt => $val ) {
1001  if ( !$this->supportsOption( $opt ) ) {
1002  $this->error( "Unexpected option $opt!" );
1003  $die = true;
1004  }
1005  }
1006  }
1007 
1008  if ( $die ) {
1009  $this->maybeHelp( true );
1010  }
1011  }
1012 
1016  protected function loadSpecialVars() {
1017  if ( $this->hasOption( 'dbuser' ) ) {
1018  $this->mDbUser = $this->getOption( 'dbuser' );
1019  }
1020  if ( $this->hasOption( 'dbpass' ) ) {
1021  $this->mDbPass = $this->getOption( 'dbpass' );
1022  }
1023  if ( $this->hasOption( 'quiet' ) ) {
1024  $this->mQuiet = true;
1025  }
1026  if ( $this->hasOption( 'batch-size' ) ) {
1027  $this->mBatchSize = intval( $this->getOption( 'batch-size' ) );
1028  }
1029  }
1030 
1035  protected function maybeHelp( $force = false ) {
1036  if ( !$force && !$this->hasOption( 'help' ) ) {
1037  return;
1038  }
1039 
1040  $screenWidth = 80; // TODO: Calculate this!
1041  $tab = " ";
1042  $descWidth = $screenWidth - ( 2 * strlen( $tab ) );
1043 
1044  ksort( $this->mParams );
1045  $this->mQuiet = false;
1046 
1047  // Description ...
1048  if ( $this->mDescription ) {
1049  $this->output( "\n" . wordwrap( $this->mDescription, $screenWidth ) . "\n" );
1050  }
1051  $output = "\nUsage: php " . basename( $this->mSelf );
1052 
1053  // ... append parameters ...
1054  if ( $this->mParams ) {
1055  $output .= " [--" . implode( "|--", array_keys( $this->mParams ) ) . "]";
1056  }
1057 
1058  // ... and append arguments.
1059  if ( $this->mArgList ) {
1060  $output .= ' ';
1061  foreach ( $this->mArgList as $k => $arg ) {
1062  if ( $arg['require'] ) {
1063  $output .= '<' . $arg['name'] . '>';
1064  } else {
1065  $output .= '[' . $arg['name'] . ']';
1066  }
1067  if ( $k < count( $this->mArgList ) - 1 ) {
1068  $output .= ' ';
1069  }
1070  }
1071  }
1072  $this->output( "$output\n\n" );
1073 
1074  # TODO abstract some repetitive code below
1075 
1076  // Generic parameters
1077  $this->output( "Generic maintenance parameters:\n" );
1078  foreach ( $this->mGenericParameters as $par => $info ) {
1079  if ( $info['shortName'] !== false ) {
1080  $par .= " (-{$info['shortName']})";
1081  }
1082  $this->output(
1083  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1084  "\n$tab$tab" ) . "\n"
1085  );
1086  }
1087  $this->output( "\n" );
1088 
1089  $scriptDependantParams = $this->mDependantParameters;
1090  if ( count( $scriptDependantParams ) > 0 ) {
1091  $this->output( "Script dependant parameters:\n" );
1092  // Parameters description
1093  foreach ( $scriptDependantParams as $par => $info ) {
1094  if ( $info['shortName'] !== false ) {
1095  $par .= " (-{$info['shortName']})";
1096  }
1097  $this->output(
1098  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1099  "\n$tab$tab" ) . "\n"
1100  );
1101  }
1102  $this->output( "\n" );
1103  }
1104 
1105  // Script specific parameters not defined on construction by
1106  // Maintenance::addDefaultParams()
1107  $scriptSpecificParams = array_diff_key(
1108  # all script parameters:
1109  $this->mParams,
1110  # remove the Maintenance default parameters:
1111  $this->mGenericParameters,
1112  $this->mDependantParameters
1113  );
1114  if ( count( $scriptSpecificParams ) > 0 ) {
1115  $this->output( "Script specific parameters:\n" );
1116  // Parameters description
1117  foreach ( $scriptSpecificParams as $par => $info ) {
1118  if ( $info['shortName'] !== false ) {
1119  $par .= " (-{$info['shortName']})";
1120  }
1121  $this->output(
1122  wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
1123  "\n$tab$tab" ) . "\n"
1124  );
1125  }
1126  $this->output( "\n" );
1127  }
1128 
1129  // Print arguments
1130  if ( count( $this->mArgList ) > 0 ) {
1131  $this->output( "Arguments:\n" );
1132  // Arguments description
1133  foreach ( $this->mArgList as $info ) {
1134  $openChar = $info['require'] ? '<' : '[';
1135  $closeChar = $info['require'] ? '>' : ']';
1136  $this->output(
1137  wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " .
1138  $info['desc'], $descWidth, "\n$tab$tab" ) . "\n"
1139  );
1140  }
1141  $this->output( "\n" );
1142  }
1143 
1144  die( 1 );
1145  }
1146 
1150  public function finalSetup() {
1154 
1155  # Turn off output buffering again, it might have been turned on in the settings files
1156  if ( ob_get_level() ) {
1157  ob_end_flush();
1158  }
1159  # Same with these
1160  $wgCommandLineMode = true;
1161 
1162  # Override $wgServer
1163  if ( $this->hasOption( 'server' ) ) {
1164  $wgServer = $this->getOption( 'server', $wgServer );
1165  }
1166 
1167  # If these were passed, use them
1168  if ( $this->mDbUser ) {
1169  $wgDBadminuser = $this->mDbUser;
1170  }
1171  if ( $this->mDbPass ) {
1172  $wgDBadminpassword = $this->mDbPass;
1173  }
1174  if ( $this->hasOption( 'dbgroupdefault' ) ) {
1175  $wgDBDefaultGroup = $this->getOption( 'dbgroupdefault', null );
1176 
1177  MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
1178  }
1179 
1180  if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) {
1181  $wgDBuser = $wgDBadminuser;
1182  $wgDBpassword = $wgDBadminpassword;
1183 
1184  if ( $wgDBservers ) {
1188  foreach ( $wgDBservers as $i => $server ) {
1189  $wgDBservers[$i]['user'] = $wgDBuser;
1190  $wgDBservers[$i]['password'] = $wgDBpassword;
1191  }
1192  }
1193  if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
1194  $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
1195  $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
1196  }
1197  MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
1198  }
1199 
1200  # Apply debug settings
1201  if ( $this->hasOption( 'mwdebug' ) ) {
1202  require __DIR__ . '/../includes/DevelopmentSettings.php';
1203  }
1204 
1205  // Per-script profiling; useful for debugging
1206  $this->activateProfiler();
1207 
1208  $this->afterFinalSetup();
1209 
1210  $wgShowExceptionDetails = true;
1211  $wgShowHostnames = true;
1212 
1213  Wikimedia\suppressWarnings();
1214  set_time_limit( 0 );
1215  Wikimedia\restoreWarnings();
1216 
1217  $this->adjustMemoryLimit();
1218  }
1219 
1223  protected function afterFinalSetup() {
1224  if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
1225  call_user_func( MW_CMDLINE_CALLBACK );
1226  }
1227  }
1228 
1233  public function globals() {
1234  if ( $this->hasOption( 'globals' ) ) {
1235  print_r( $GLOBALS );
1236  }
1237  }
1238 
1243  public function loadSettings() {
1244  global $wgCommandLineMode, $IP;
1245 
1246  if ( isset( $this->mOptions['conf'] ) ) {
1247  $settingsFile = $this->mOptions['conf'];
1248  } elseif ( defined( "MW_CONFIG_FILE" ) ) {
1249  $settingsFile = MW_CONFIG_FILE;
1250  } else {
1251  $settingsFile = "$IP/LocalSettings.php";
1252  }
1253  if ( isset( $this->mOptions['wiki'] ) ) {
1254  $bits = explode( '-', $this->mOptions['wiki'] );
1255  if ( count( $bits ) == 1 ) {
1256  $bits[] = '';
1257  }
1258  define( 'MW_DB', $bits[0] );
1259  define( 'MW_PREFIX', $bits[1] );
1260  } elseif ( isset( $this->mOptions['server'] ) ) {
1261  // Provide the option for site admins to detect and configure
1262  // multiple wikis based on server names. This offers --server
1263  // as alternative to --wiki.
1264  // See https://www.mediawiki.org/wiki/Manual:Wiki_family
1265  $_SERVER['SERVER_NAME'] = $this->mOptions['server'];
1266  }
1267 
1268  if ( !is_readable( $settingsFile ) ) {
1269  $this->fatalError( "A copy of your installation's LocalSettings.php\n" .
1270  "must exist and be readable in the source directory.\n" .
1271  "Use --conf to specify it." );
1272  }
1273  $wgCommandLineMode = true;
1274 
1275  return $settingsFile;
1276  }
1277 
1283  public function purgeRedundantText( $delete = true ) {
1284  # Data should come off the master, wrapped in a transaction
1285  $dbw = $this->getDB( DB_MASTER );
1286  $this->beginTransaction( $dbw, __METHOD__ );
1287 
1288  # Get "active" text records from the revisions table
1289  $cur = [];
1290  $this->output( 'Searching for active text records in revisions table...' );
1291  $res = $dbw->select( 'revision', 'rev_text_id', [], __METHOD__, [ 'DISTINCT' ] );
1292  foreach ( $res as $row ) {
1293  $cur[] = $row->rev_text_id;
1294  }
1295  $this->output( "done.\n" );
1296 
1297  # Get "active" text records from the archive table
1298  $this->output( 'Searching for active text records in archive table...' );
1299  $res = $dbw->select( 'archive', 'ar_text_id', [], __METHOD__, [ 'DISTINCT' ] );
1300  foreach ( $res as $row ) {
1301  # old pre-MW 1.5 records can have null ar_text_id's.
1302  if ( $row->ar_text_id !== null ) {
1303  $cur[] = $row->ar_text_id;
1304  }
1305  }
1306  $this->output( "done.\n" );
1307 
1308  # Get the IDs of all text records not in these sets
1309  $this->output( 'Searching for inactive text records...' );
1310  $cond = 'old_id NOT IN ( ' . $dbw->makeList( $cur ) . ' )';
1311  $res = $dbw->select( 'text', 'old_id', [ $cond ], __METHOD__, [ 'DISTINCT' ] );
1312  $old = [];
1313  foreach ( $res as $row ) {
1314  $old[] = $row->old_id;
1315  }
1316  $this->output( "done.\n" );
1317 
1318  # Inform the user of what we're going to do
1319  $count = count( $old );
1320  $this->output( "$count inactive items found.\n" );
1321 
1322  # Delete as appropriate
1323  if ( $delete && $count ) {
1324  $this->output( 'Deleting...' );
1325  $dbw->delete( 'text', [ 'old_id' => $old ], __METHOD__ );
1326  $this->output( "done.\n" );
1327  }
1328 
1329  # Done
1330  $this->commitTransaction( $dbw, __METHOD__ );
1331  }
1332 
1337  protected function getDir() {
1338  return __DIR__;
1339  }
1340 
1351  protected function getDB( $db, $groups = [], $wiki = false ) {
1352  if ( is_null( $this->mDb ) ) {
1353  return wfGetDB( $db, $groups, $wiki );
1354  } else {
1355  return $this->mDb;
1356  }
1357  }
1358 
1364  public function setDB( IDatabase $db ) {
1365  $this->mDb = $db;
1366  }
1367 
1378  protected function beginTransaction( IDatabase $dbw, $fname ) {
1379  $dbw->begin( $fname );
1380  }
1381 
1393  protected function commitTransaction( IDatabase $dbw, $fname ) {
1394  $dbw->commit( $fname );
1395  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
1396  $waitSucceeded = $lbFactory->waitForReplication(
1397  [ 'timeout' => 30, 'ifWritesSince' => $this->lastReplicationWait ]
1398  );
1399  $this->lastReplicationWait = microtime( true );
1400  return $waitSucceeded;
1401  }
1402 
1413  protected function rollbackTransaction( IDatabase $dbw, $fname ) {
1414  $dbw->rollback( $fname );
1415  }
1416 
1421  private function lockSearchindex( $db ) {
1422  $write = [ 'searchindex' ];
1423  $read = [
1424  'page',
1425  'revision',
1426  'text',
1427  'interwiki',
1428  'l10n_cache',
1429  'user',
1430  'page_restrictions'
1431  ];
1432  $db->lockTables( $read, $write, __CLASS__ . '::' . __METHOD__ );
1433  }
1434 
1439  private function unlockSearchindex( $db ) {
1440  $db->unlockTables( __CLASS__ . '::' . __METHOD__ );
1441  }
1442 
1448  private function relockSearchindex( $db ) {
1449  $this->unlockSearchindex( $db );
1450  $this->lockSearchindex( $db );
1451  }
1452 
1460  public function updateSearchIndex( $maxLockTime, $callback, $dbw, $results ) {
1461  $lockTime = time();
1462 
1463  # Lock searchindex
1464  if ( $maxLockTime ) {
1465  $this->output( " --- Waiting for lock ---" );
1466  $this->lockSearchindex( $dbw );
1467  $lockTime = time();
1468  $this->output( "\n" );
1469  }
1470 
1471  # Loop through the results and do a search update
1472  foreach ( $results as $row ) {
1473  # Allow reads to be processed
1474  if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
1475  $this->output( " --- Relocking ---" );
1476  $this->relockSearchindex( $dbw );
1477  $lockTime = time();
1478  $this->output( "\n" );
1479  }
1480  call_user_func( $callback, $dbw, $row );
1481  }
1482 
1483  # Unlock searchindex
1484  if ( $maxLockTime ) {
1485  $this->output( " --- Unlocking --" );
1486  $this->unlockSearchindex( $dbw );
1487  $this->output( "\n" );
1488  }
1489  }
1490 
1497  public function updateSearchIndexForPage( $dbw, $pageId ) {
1498  // Get current revision
1499  $rev = Revision::loadFromPageId( $dbw, $pageId );
1500  $title = null;
1501  if ( $rev ) {
1502  $titleObj = $rev->getTitle();
1503  $title = $titleObj->getPrefixedDBkey();
1504  $this->output( "$title..." );
1505  # Update searchindex
1506  $u = new SearchUpdate( $pageId, $titleObj->getText(), $rev->getContent() );
1507  $u->doUpdate();
1508  $this->output( "\n" );
1509  }
1510 
1511  return $title;
1512  }
1513 
1524  protected function countDown( $seconds ) {
1525  if ( $this->isQuiet() ) {
1526  return;
1527  }
1528  for ( $i = $seconds; $i >= 0; $i-- ) {
1529  if ( $i != $seconds ) {
1530  $this->output( str_repeat( "\x08", strlen( $i + 1 ) ) );
1531  }
1532  $this->output( $i );
1533  if ( $i ) {
1534  sleep( 1 );
1535  }
1536  }
1537  $this->output( "\n" );
1538  }
1539 
1548  public static function posix_isatty( $fd ) {
1549  if ( !function_exists( 'posix_isatty' ) ) {
1550  return !$fd;
1551  } else {
1552  return posix_isatty( $fd );
1553  }
1554  }
1555 
1561  public static function readconsole( $prompt = '> ' ) {
1562  static $isatty = null;
1563  if ( is_null( $isatty ) ) {
1564  $isatty = self::posix_isatty( 0 /*STDIN*/ );
1565  }
1566 
1567  if ( $isatty && function_exists( 'readline' ) ) {
1568  return readline( $prompt );
1569  } else {
1570  if ( $isatty ) {
1571  $st = self::readlineEmulation( $prompt );
1572  } else {
1573  if ( feof( STDIN ) ) {
1574  $st = false;
1575  } else {
1576  $st = fgets( STDIN, 1024 );
1577  }
1578  }
1579  if ( $st === false ) {
1580  return false;
1581  }
1582  $resp = trim( $st );
1583 
1584  return $resp;
1585  }
1586  }
1587 
1593  private static function readlineEmulation( $prompt ) {
1594  $bash = ExecutableFinder::findInDefaultPaths( 'bash' );
1595  if ( !wfIsWindows() && $bash ) {
1596  $retval = false;
1597  $encPrompt = wfEscapeShellArg( $prompt );
1598  $command = "read -er -p $encPrompt && echo \"\$REPLY\"";
1599  $encCommand = wfEscapeShellArg( $command );
1600  $line = wfShellExec( "$bash -c $encCommand", $retval, [], [ 'walltime' => 0 ] );
1601 
1602  if ( $retval == 0 ) {
1603  return $line;
1604  } elseif ( $retval == 127 ) {
1605  // Couldn't execute bash even though we thought we saw it.
1606  // Shell probably spit out an error message, sorry :(
1607  // Fall through to fgets()...
1608  } else {
1609  // EOF/ctrl+D
1610  return false;
1611  }
1612  }
1613 
1614  // Fallback... we'll have no editing controls, EWWW
1615  if ( feof( STDIN ) ) {
1616  return false;
1617  }
1618  print $prompt;
1619 
1620  return fgets( STDIN, 1024 );
1621  }
1622 
1630  public static function getTermSize() {
1631  $default = [ 80, 50 ];
1632  if ( wfIsWindows() ) {
1633  return $default;
1634  }
1635  if ( Shell::isDisabled() ) {
1636  return $default;
1637  }
1638  // It's possible to get the screen size with VT-100 terminal escapes,
1639  // but reading the responses is not possible without setting raw mode
1640  // (unless you want to require the user to press enter), and that
1641  // requires an ioctl(), which we can't do. So we have to shell out to
1642  // something that can do the relevant syscalls. There are a few
1643  // options. Linux and Mac OS X both have "stty size" which does the
1644  // job directly.
1645  $result = Shell::command( 'stty', 'size' )
1646  ->execute();
1647  if ( $result->getExitCode() !== 0 ) {
1648  return $default;
1649  }
1650  if ( !preg_match( '/^(\d+) (\d+)$/', $result->getStdout(), $m ) ) {
1651  return $default;
1652  }
1653  return [ intval( $m[2] ), intval( $m[1] ) ];
1654  }
1655 
1660  public static function requireTestsAutoloader() {
1661  require_once __DIR__ . '/../tests/common/TestsAutoLoader.php';
1662  }
1663 }
1664 
1669  protected $mSelf = "FakeMaintenanceScript";
1670 
1671  public function execute() {
1672  }
1673 }
1674 
1679 abstract class LoggedUpdateMaintenance extends Maintenance {
1680  public function __construct() {
1681  parent::__construct();
1682  $this->addOption( 'force', 'Run the update even if it was completed already' );
1683  $this->setBatchSize( 200 );
1684  }
1685 
1686  public function execute() {
1687  $db = $this->getDB( DB_MASTER );
1688  $key = $this->getUpdateKey();
1689 
1690  if ( !$this->hasOption( 'force' )
1691  && $db->selectRow( 'updatelog', '1', [ 'ul_key' => $key ], __METHOD__ )
1692  ) {
1693  $this->output( "..." . $this->updateSkippedMessage() . "\n" );
1694 
1695  return true;
1696  }
1697 
1698  if ( !$this->doDBUpdates() ) {
1699  return false;
1700  }
1701 
1702  $db->insert( 'updatelog', [ 'ul_key' => $key ], __METHOD__, 'IGNORE' );
1703 
1704  return true;
1705  }
1706 
1711  protected function updateSkippedMessage() {
1712  $key = $this->getUpdateKey();
1713 
1714  return "Update '{$key}' already logged as completed.";
1715  }
1716 
1722  abstract protected function doDBUpdates();
1723 
1728  abstract protected function getUpdateKey();
1729 }
commitTransaction(IDatabase $dbw, $fname)
Commit the transcation on a DB handle and wait for replica DBs to catch up.
const STDIN_ALL
Definition: Maintenance.php:70
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:785
getArg( $argId=0, $default=null)
Get an argument.
const DB_NONE
Constants for DB access type.
Definition: Maintenance.php:65
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:36
maybeHelp( $force=false)
Maybe show the help.
checkRequiredExtensions()
Verify that the required extensions are installed.
if(is_array( $mode)) switch( $mode) $input
$wgDBDefaultGroup
Default group to use when getting database connections.
setParam(&$options, $option, $value)
Helper function used solely by loadParamsAndArgs to prevent code duplication.
error( $err, $die=0)
Throw an error to the user.
runChild( $maintClass, $classFile=null)
Run a child maintenance script.
static setLBFactoryTriggers(LBFactory $LBFactory, Config $config)
$IP
Definition: WebStart.php:41
An interface for generating database load balancers.
Definition: LBFactory.php:39
$wgDBadminpassword
Separate password for maintenance tasks.
lockSearchindex( $db)
Lock the search index.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
getOption( $name, $default=null)
Get an option, or return the default.
getDir()
Get the maintenance directory.
array $orderedOptions
Used to read the options in the order they were passed.
$command
Definition: cdb.php:65
static instance()
Singleton.
Definition: Profiler.php:62
$wgDBpassword
Database user&#39;s password.
wfHostname()
Fetch server name for use in error reporting etc.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: maintenance.txt:39
getName()
Get the script&#39;s name.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
addDefaultParams()
Add the default parameters to the scripts.
finalSetup()
Handle some last-minute setup here.
rollbackTransaction(IDatabase $dbw, $fname)
Rollback the transcation on a DB handle.
afterFinalSetup()
Execute a callback function at the end of initialisation.
$value
static readconsole( $prompt='> ')
Prompt the console for input.
requireExtension( $name)
Indicate that the specified extension must be loaded before the script can run.
setConfig(Config $config)
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
updateSearchIndex( $maxLockTime, $callback, $dbw, $results)
Perform a search index update with locking.
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that&#39;s attached to a given page.
Definition: Revision.php:261
setBatchSize( $s=0)
Set the batch size.
$wgDBuser
Database username.
loadSpecialVars()
Handle the special variables that are global to all scripts.
$wgShowExceptionDetails
If set to true, uncaught exceptions will print the exception message and a complete stack trace to ou...
wfEntryPointCheck( $format='text', $scriptPath='/')
Check PHP version and that external dependencies are installed, and display an informative error if e...
updateSearchIndexForPage( $dbw, $pageId)
Update the searchindex table for a given pageid.
hasOption( $name)
Checks to see if a particular option exists.
relockSearchindex( $db)
Unlock and lock again Since the lock is low-priority, queued reads will be able to complete...
loadWithArgv( $argv)
Load params and arguments from a given array of command-line arguments.
wfIsWindows()
Check if the operating system is Windows.
outputChanneled( $msg, $channel=null)
Message outputter with channeled message support.
const DB_MASTER
Definition: defines.php:26
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
array $requiredExtensions
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place $output
Definition: hooks.txt:2221
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1994
$maintClass
Definition: Maintenance.php:43
if( $line===false) $args
Definition: cdb.php:64
cleanupChanneled()
Clean up channeled output.
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
static replaceStubInstance(Profiler $profiler)
Replace the current profiler with $profiler if no non-stub profiler is set.
Definition: Profiler.php:98
wfShellExec( $cmd, &$retval=null, $environ=[], $limits=[], $options=[])
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
supportsOption( $name)
Checks to see if a particular option in supported.
Fake maintenance wrapper, mostly used for the web installer/updater.
activateProfiler()
Activate the profiler (assuming $wgProfiler is set)
static requireTestsAutoloader()
Call this to set up the autoloader to allow classes to be used from the tests directory.
hasArg( $argId=0)
Does a given argument exist?
$wgTrxProfilerLimits
Performance expectations for DB usage.
Interface for configuration instances.
Definition: Config.php:28
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2221
$mAllowUnregisteredOptions
Definition: Maintenance.php:88
$self
$res
Definition: database.txt:21
float $lastReplicationWait
UNIX timestamp.
addDescription( $text)
Set the description text.
const DB_ADMIN
Definition: Maintenance.php:67
setup()
Do some sanity checking and basic setup.
setDB(IDatabase $db)
Sets database object to be returned by getDB().
clearParamsAndArgs()
Clear all params and arguments.
deleteOption( $name)
Remove an option.
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:1996
$wgDBservers
Database load balancer This is a two-dimensional array, an array of server info structures Fields are...
addArg( $arg, $description, $required=true)
Add some args that are needed.
globals()
Potentially debug globals.
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:936
adjustMemoryLimit()
Adjusts PHP&#39;s memory limit to better suit our needs, if needed.
getDB( $db, $groups=[], $wiki=false)
Returns a database to be used by current maintenance script.
$GLOBALS['IP']
commit( $fname=__METHOD__, $flush='')
Commits a transaction previously started using begin().
output( $out, $channel=null)
Throw some output to the user.
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:1780
resource $fileHandle
Used when creating separate schema files.
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
unlockSearchindex( $db)
Unlock the tables.
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings, LocalSettings).
Definition: Setup.php:121
static findInDefaultPaths( $names, $versionInfo=false)
Same as locateExecutable(), but checks in getPossibleBinPaths() by default.
static shouldExecute()
Should we execute the maintenance script, or just allow it to be included as a standalone class...
static tryOpportunisticExecute( $mode='run')
Run all deferred updates immediately if there are no DB writes active.
const DB_STD
Definition: Maintenance.php:66
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
static getTermSize()
Get the terminal size as a two-element array where the first element is the width (number of columns)...
static readlineEmulation( $prompt)
Emulate readline()
static posix_isatty( $fd)
Wrapper for posix_isatty() We default as considering stdin a tty (for nice readline methods) but trea...
Class for scripts that perform database maintenance and want to log the update in updatelog so we can...
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
$wgDBadminuser
Separate username for maintenance tasks.
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
memoryLimit()
Normally we disable the memory_limit when running admin scripts.
$wgProfileLimit
Only record profiling info for pages that took longer than this.
static emitBufferedStatsdData(IBufferingStatsdDataFactory $stats, Config $config)
Send out any buffered statsd data according to sampling rules.
Definition: MediaWiki.php:930
int $mBatchSize
Batch size.
getBatchSize()
Returns batch size.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$line
Definition: cdb.php:59
global $wgCommandLineMode
setAgentAndTriggers()
Set triggers like when to try to run deferred updates.
countDown( $seconds)
Count down from $seconds to zero on the terminal, with a one-second pause between showing each number...
updateSkippedMessage()
Message to show that the update was done already and was just skipped.
MediaWiki Logger LoggerFactory implements a PSR [0] compatible message logging system Named Psr Log LoggerInterface instances can be obtained from the MediaWiki Logger LoggerFactory::getInstance() static method. MediaWiki\Logger\LoggerFactory expects a class implementing the MediaWiki\Logger\Spi interface to act as a factory for new Psr\Log\LoggerInterface instances. The "Spi" in MediaWiki\Logger\Spi stands for "service provider interface". An SPI is an API intended to be implemented or extended by a third party. This software design pattern is intended to enable framework extension and replaceable components. It is specifically used in the MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging implementations to be easily integrated with MediaWiki. The service provider interface allows the backend logging library to be implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the classname of the default MediaWiki\Logger\Spi implementation to be loaded at runtime. This can either be the name of a class implementing the MediaWiki\Logger\Spi with a zero argument const ructor or a callable that will return an MediaWiki\Logger\Spi instance. Alternately the MediaWiki\Logger\LoggerFactory MediaWiki Logger LoggerFactory
Definition: logger.txt:5
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
wfEscapeShellArg(... $args)
Version of escapeshellarg() that works better on Windows.
getStdin( $len=null)
Return input from stdin.
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:276
$wgServer
URL of the server.
$wgLBFactoryConf
Load balancer factory configuration To set up a multi-master wiki farm, set the class here to somethi...
loadSettings()
Generic setup for most installs.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
purgeRedundantText( $delete=true)
Support function for cleaning up redundant text records.
$wgProfiler
Profiler configuration.
validateParamsAndArgs()
Run some validation checks on the params, etc.
Config $config
Accessible via getConfig()
IMaintainableDatabase $mDb
Used by getDB() / setDB()
loadParamsAndArgs( $self=null, $opts=null, $args=null)
Process command line arguments $mOptions becomes an array with keys set to the option names $mArgs be...
getDbType()
Does the script need different DB access? By default, we give Maintenance scripts normal rights to th...
public function execute()
Definition: maintenance.txt:45
__construct()
Default constructor.
beginTransaction(IDatabase $dbw, $fname)
Begin a transcation on a DB.
setAllowUnregisteredOptions( $allow)
Sets whether to allow unregistered options, which are options passed to a script that do not match an...
$wgShowHostnames
Expose backend server host names through the API and various HTML comments.