Merge "Set $_SERVER['SERVER_NAME'] to the value set by --server"
[lhc/web/wiklou.git] / maintenance / Maintenance.php
index ec54993..86336cf 100644 (file)
@@ -50,7 +50,6 @@ use Wikimedia\Rdbms\IMaintainableDatabase;
  * is the execute() method. See docs/maintenance.txt for more info
  * and a quick demo of how to use it.
  *
- * @author Chad Horohoe <chad@anyonecanedit.org>
  * @since 1.16
  * @ingroup Maintenance
  */
@@ -183,7 +182,7 @@ abstract class Maintenance {
                if ( $count < 2 ) {
                        return false; // sanity
                }
-               if ( $bt[0]['class'] !== 'Maintenance' || $bt[0]['function'] !== 'shouldExecute' ) {
+               if ( $bt[0]['class'] !== self::class || $bt[0]['function'] !== 'shouldExecute' ) {
                        return false; // last call should be to this function
                }
                $includeFuncs = [ 'require_once', 'require', 'include', 'include_once' ];
@@ -307,6 +306,17 @@ abstract class Maintenance {
                return $this->hasArg( $argId ) ? $this->mArgs[$argId] : $default;
        }
 
+       /**
+        * Returns batch size
+        *
+        * @since 1.31
+        *
+        * @return int|null
+        */
+       protected function getBatchSize() {
+               return $this->mBatchSize;
+       }
+
        /**
         * Set the batch size.
         * @param int $s The number of operations to do in a batch
@@ -371,6 +381,15 @@ abstract class Maintenance {
         * @param mixed $channel Unique identifier for the channel. See function outputChanneled.
         */
        protected function output( $out, $channel = null ) {
+               // This is sometimes called very early, before Setup.php is included.
+               if ( class_exists( MediaWikiServices::class ) ) {
+                       // Try to periodically flush buffered metrics to avoid OOMs
+                       $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+                       if ( $stats->getDataCount() > 1000 ) {
+                               MediaWiki::emitBufferedStatsdData( $stats, $this->getConfig() );
+                       }
+               }
+
                if ( $this->mQuiet ) {
                        return;
                }
@@ -387,19 +406,31 @@ abstract class Maintenance {
         * Throw an error to the user. Doesn't respect --quiet, so don't use
         * this for non-error output
         * @param string $err The error to display
-        * @param int $die If > 0, go ahead and die out using this int as the code
+        * @param int $die Deprecated since 1.31, use Maintenance::fatalError() instead
         */
        protected function error( $err, $die = 0 ) {
+               if ( intval( $die ) !== 0 ) {
+                       wfDeprecated( __METHOD__ . '( $err, $die )', '1.31' );
+                       $this->fatalError( $err, intval( $die ) );
+               }
                $this->outputChanneled( false );
-               if ( PHP_SAPI == 'cli' ) {
+               if ( PHP_SAPI == 'cli' || PHP_SAPI == 'phpdbg' ) {
                        fwrite( STDERR, $err . "\n" );
                } else {
                        print $err;
                }
-               $die = intval( $die );
-               if ( $die > 0 ) {
-                       die( $die );
-               }
+       }
+
+       /**
+        * Output a message and terminate the current script.
+        *
+        * @param string $msg Error message
+        * @param int $exitCode PHP exit status. Should be in range 1-254.
+        * @since 1.31
+        */
+       protected function fatalError( $msg, $exitCode = 1 ) {
+               $this->error( $msg );
+               exit( $exitCode );
        }
 
        private $atLineStart = true;
@@ -548,7 +579,7 @@ abstract class Maintenance {
                        $joined = implode( ', ', $missing );
                        $msg = "The following extensions are required to be installed "
                                . "for this script to run: $joined. Please enable them and then try again.";
-                       $this->error( $msg, 1 );
+                       $this->fatalError( $msg );
                }
        }
 
@@ -569,36 +600,41 @@ abstract class Maintenance {
                $lbFactory->setAgentName(
                        mb_strlen( $agent ) > 15 ? mb_substr( $agent, 0, 15 ) . '...' : $agent
                );
-               self::setLBFactoryTriggers( $lbFactory );
+               self::setLBFactoryTriggers( $lbFactory, $this->getConfig() );
        }
 
        /**
         * @param LBFactory $LBFactory
+        * @param Config $config
         * @since 1.28
         */
-       public static function setLBFactoryTriggers( LBFactory $LBFactory ) {
+       public static function setLBFactoryTriggers( LBFactory $LBFactory, Config $config ) {
+               $services = MediaWikiServices::getInstance();
+               $stats = $services->getStatsdDataFactory();
                // Hook into period lag checks which often happen in long-running scripts
-               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $lbFactory->setWaitForReplicationListener(
                        __METHOD__,
-                       function () {
-                               global $wgCommandLineMode;
+                       function () use ( $stats, $config ) {
                                // Check config in case of JobRunner and unit tests
-                               if ( $wgCommandLineMode ) {
+                               if ( $config->get( 'CommandLineMode' ) ) {
                                        DeferredUpdates::tryOpportunisticExecute( 'run' );
                                }
+                               // Try to periodically flush buffered metrics to avoid OOMs
+                               MediaWiki::emitBufferedStatsdData( $stats, $config );
                        }
                );
                // Check for other windows to run them. A script may read or do a few writes
                // to the master but mostly be writing to something else, like a file store.
                $lbFactory->getMainLB()->setTransactionListener(
                        __METHOD__,
-                       function ( $trigger ) {
-                               global $wgCommandLineMode;
+                       function ( $trigger ) use ( $stats, $config ) {
                                // Check config in case of JobRunner and unit tests
-                               if ( $wgCommandLineMode && $trigger === IDatabase::TRIGGER_COMMIT ) {
+                               if ( $config->get( 'CommandLineMode' ) && $trigger === IDatabase::TRIGGER_COMMIT ) {
                                        DeferredUpdates::tryOpportunisticExecute( 'run' );
                                }
+                               // Try to periodically flush buffered metrics to avoid OOMs
+                               MediaWiki::emitBufferedStatsdData( $stats, $config );
                        }
                );
        }
@@ -640,18 +676,19 @@ abstract class Maintenance {
                global $IP, $wgCommandLineMode, $wgRequestTime;
 
                # Abort if called from a web server
-               if ( isset( $_SERVER ) && isset( $_SERVER['REQUEST_METHOD'] ) ) {
-                       $this->error( 'This script must be run from the command line', true );
+               # wfIsCLI() is not available yet
+               if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
+                       $this->fatalError( 'This script must be run from the command line' );
                }
 
                if ( $IP === null ) {
-                       $this->error( "\$IP not set, aborting!\n" .
-                               '(Did you forget to call parent::__construct() in your maintenance script?)', 1 );
+                       $this->fatalError( "\$IP not set, aborting!\n" .
+                               '(Did you forget to call parent::__construct() in your maintenance script?)' );
                }
 
                # Make sure we can handle script parameters
                if ( !defined( 'HPHP_VERSION' ) && !ini_get( 'register_argc_argv' ) ) {
-                       $this->error( 'Cannot get command line arguments, register_argc_argv is set to false', true );
+                       $this->fatalError( 'Cannot get command line arguments, register_argc_argv is set to false' );
                }
 
                // Send PHP warnings and errors to stderr instead of stdout.
@@ -1166,9 +1203,9 @@ abstract class Maintenance {
                }
 
                if ( !is_readable( $settingsFile ) ) {
-                       $this->error( "A copy of your installation's LocalSettings.php\n" .
+                       $this->fatalError( "A copy of your installation's LocalSettings.php\n" .
                                "must exist and be readable in the source directory.\n" .
-                               "Use --conf to specify it.", true );
+                               "Use --conf to specify it." );
                }
                if ( isset( $this->mOptions['server'] ) ) {
                        $_SERVER['SERVER_NAME'] = $this->mOptions['server'];