[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/maintenance/ -> backup.inc (source)

   1  <?php
   2  /**
   3   * Base classes for database dumpers
   4   *
   5   * Copyright © 2005 Brion Vibber <[email protected]>
   6   * https://www.mediawiki.org/
   7   *
   8   * This program is free software; you can redistribute it and/or modify
   9   * it under the terms of the GNU General Public License as published by
  10   * the Free Software Foundation; either version 2 of the License, or
  11   * (at your option) any later version.
  12   *
  13   * This program is distributed in the hope that it will be useful,
  14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16   * GNU General Public License for more details.
  17   *
  18   * You should have received a copy of the GNU General Public License along
  19   * with this program; if not, write to the Free Software Foundation, Inc.,
  20   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21   * http://www.gnu.org/copyleft/gpl.html
  22   *
  23   * @file
  24   * @ingroup Dump Maintenance
  25   */
  26  
  27  /**
  28   * @ingroup Dump Maintenance
  29   */
  30  class DumpDBZip2Output extends DumpPipeOutput {
  31  	function __construct( $file ) {
  32          parent::__construct( "dbzip2", $file );
  33      }
  34  }
  35  
  36  /**
  37   * @ingroup Dump Maintenance
  38   */
  39  class BackupDumper {
  40      public $reporting = true;
  41      public $pages = null; // all pages
  42      public $skipHeader = false; // don't output <mediawiki> and <siteinfo>
  43      public $skipFooter = false; // don't output </mediawiki>
  44      public $startId = 0;
  45      public $endId = 0;
  46      public $revStartId = 0;
  47      public $revEndId = 0;
  48      public $dumpUploads = false;
  49      public $dumpUploadFileContents = false;
  50  
  51      protected $reportingInterval = 100;
  52      protected $pageCount = 0;
  53      protected $revCount = 0;
  54      protected $server = null; // use default
  55      protected $sink = null; // Output filters
  56      protected $lastTime = 0;
  57      protected $pageCountLast = 0;
  58      protected $revCountLast = 0;
  59  
  60      protected $outputTypes = array();
  61      protected $filterTypes = array();
  62  
  63      protected $ID = 0;
  64  
  65      /**
  66       * The dependency-injected database to use.
  67       *
  68       * @var DatabaseBase|null
  69       *
  70       * @see self::setDb
  71       */
  72      protected $forcedDb = null;
  73  
  74      /** @var LoadBalancer */
  75      protected $lb;
  76  
  77      // @todo Unused?
  78      private $stubText = false; // include rev_text_id instead of text; for 2-pass dump
  79  
  80  	function __construct( $args ) {
  81          $this->stderr = fopen( "php://stderr", "wt" );
  82  
  83          // Built-in output and filter plugins
  84          $this->registerOutput( 'file', 'DumpFileOutput' );
  85          $this->registerOutput( 'gzip', 'DumpGZipOutput' );
  86          $this->registerOutput( 'bzip2', 'DumpBZip2Output' );
  87          $this->registerOutput( 'dbzip2', 'DumpDBZip2Output' );
  88          $this->registerOutput( '7zip', 'Dump7ZipOutput' );
  89  
  90          $this->registerFilter( 'latest', 'DumpLatestFilter' );
  91          $this->registerFilter( 'notalk', 'DumpNotalkFilter' );
  92          $this->registerFilter( 'namespace', 'DumpNamespaceFilter' );
  93  
  94          $this->sink = $this->processArgs( $args );
  95      }
  96  
  97      /**
  98       * @param string $name
  99       * @param string $class Name of output filter plugin class
 100       */
 101  	function registerOutput( $name, $class ) {
 102          $this->outputTypes[$name] = $class;
 103      }
 104  
 105      /**
 106       * @param string $name
 107       * @param string $class Name of filter plugin class
 108       */
 109  	function registerFilter( $name, $class ) {
 110          $this->filterTypes[$name] = $class;
 111      }
 112  
 113      /**
 114       * Load a plugin and register it
 115       *
 116       * @param string $class Name of plugin class; must have a static 'register'
 117       *   method that takes a BackupDumper as a parameter.
 118       * @param string $file Full or relative path to the PHP file to load, or empty
 119       */
 120  	function loadPlugin( $class, $file ) {
 121          if ( $file != '' ) {
 122              require_once $file;
 123          }
 124          $register = array( $class, 'register' );
 125          call_user_func_array( $register, array( &$this ) );
 126      }
 127  
 128      /**
 129       * @param array $args
 130       * @return array
 131       */
 132  	function processArgs( $args ) {
 133          $sink = null;
 134          $sinks = array();
 135          foreach ( $args as $arg ) {
 136              $matches = array();
 137              if ( preg_match( '/^--(.+?)(?:=(.+?)(?::(.+?))?)?$/', $arg, $matches ) ) {
 138                  wfSuppressWarnings();
 139                  list( /* $full */, $opt, $val, $param ) = $matches;
 140                  wfRestoreWarnings();
 141  
 142                  switch ( $opt ) {
 143                      case "plugin":
 144                          $this->loadPlugin( $val, $param );
 145                          break;
 146                      case "output":
 147                          if ( !is_null( $sink ) ) {
 148                              $sinks[] = $sink;
 149                          }
 150                          if ( !isset( $this->outputTypes[$val] ) ) {
 151                              $this->fatalError( "Unrecognized output sink type '$val'" );
 152                          }
 153                          $type = $this->outputTypes[$val];
 154                          $sink = new $type( $param );
 155                          break;
 156                      case "filter":
 157                          if ( is_null( $sink ) ) {
 158                              $sink = new DumpOutput();
 159                          }
 160                          if ( !isset( $this->filterTypes[$val] ) ) {
 161                              $this->fatalError( "Unrecognized filter type '$val'" );
 162                          }
 163                          $type = $this->filterTypes[$val];
 164                          $filter = new $type( $sink, $param );
 165  
 166                          // references are lame in php...
 167                          unset( $sink );
 168                          $sink = $filter;
 169  
 170                          break;
 171                      case "report":
 172                          $this->reportingInterval = intval( $val );
 173                          break;
 174                      case "server":
 175                          $this->server = $val;
 176                          break;
 177                      case "force-normal":
 178                          if ( !function_exists( 'utf8_normalize' ) ) {
 179                              $this->fatalError( "UTF-8 normalization extension not loaded. " .
 180                                  "Install or remove --force-normal parameter to use slower code." );
 181                          }
 182                          break;
 183                      default:
 184                          $this->processOption( $opt, $val, $param );
 185                  }
 186              }
 187          }
 188  
 189          if ( is_null( $sink ) ) {
 190              $sink = new DumpOutput();
 191          }
 192          $sinks[] = $sink;
 193  
 194          if ( count( $sinks ) > 1 ) {
 195              return new DumpMultiWriter( $sinks );
 196          } else {
 197              return $sink;
 198          }
 199      }
 200  
 201  	function processOption( $opt, $val, $param ) {
 202          // extension point for subclasses to add options
 203      }
 204  
 205  	function dump( $history, $text = WikiExporter::TEXT ) {
 206          # Notice messages will foul up your XML output even if they're
 207          # relatively harmless.
 208          if ( ini_get( 'display_errors' ) ) {
 209              ini_set( 'display_errors', 'stderr' );
 210          }
 211  
 212          $this->initProgress( $history );
 213  
 214          $db = $this->backupDb();
 215          $exporter = new WikiExporter( $db, $history, WikiExporter::STREAM, $text );
 216          $exporter->dumpUploads = $this->dumpUploads;
 217          $exporter->dumpUploadFileContents = $this->dumpUploadFileContents;
 218  
 219          $wrapper = new ExportProgressFilter( $this->sink, $this );
 220          $exporter->setOutputSink( $wrapper );
 221  
 222          if ( !$this->skipHeader ) {
 223              $exporter->openStream();
 224          }
 225          # Log item dumps: all or by range
 226          if ( $history & WikiExporter::LOGS ) {
 227              if ( $this->startId || $this->endId ) {
 228                  $exporter->logsByRange( $this->startId, $this->endId );
 229              } else {
 230                  $exporter->allLogs();
 231              }
 232          } elseif ( is_null( $this->pages ) ) {
 233              # Page dumps: all or by page ID range
 234              if ( $this->startId || $this->endId ) {
 235                  $exporter->pagesByRange( $this->startId, $this->endId );
 236              } elseif ( $this->revStartId || $this->revEndId ) {
 237                  $exporter->revsByRange( $this->revStartId, $this->revEndId );
 238              } else {
 239                  $exporter->allPages();
 240              }
 241          } else {
 242              # Dump of specific pages
 243              $exporter->pagesByName( $this->pages );
 244          }
 245  
 246          if ( !$this->skipFooter ) {
 247              $exporter->closeStream();
 248          }
 249  
 250          $this->report( true );
 251      }
 252  
 253      /**
 254       * Initialise starting time and maximum revision count.
 255       * We'll make ETA calculations based an progress, assuming relatively
 256       * constant per-revision rate.
 257       * @param int $history WikiExporter::CURRENT or WikiExporter::FULL
 258       */
 259  	function initProgress( $history = WikiExporter::FULL ) {
 260          $table = ( $history == WikiExporter::CURRENT ) ? 'page' : 'revision';
 261          $field = ( $history == WikiExporter::CURRENT ) ? 'page_id' : 'rev_id';
 262  
 263          $dbr = $this->forcedDb;
 264          if ( $this->forcedDb === null ) {
 265              $dbr = wfGetDB( DB_SLAVE );
 266          }
 267          $this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ );
 268          $this->startTime = microtime( true );
 269          $this->lastTime = $this->startTime;
 270          $this->ID = getmypid();
 271      }
 272  
 273      /**
 274       * @todo Fixme: the --server parameter is currently not respected, as it
 275       * doesn't seem terribly easy to ask the load balancer for a particular
 276       * connection by name.
 277       * @return DatabaseBase
 278       */
 279  	function backupDb() {
 280          if ( $this->forcedDb !== null ) {
 281              return $this->forcedDb;
 282          }
 283  
 284          $this->lb = wfGetLBFactory()->newMainLB();
 285          $db = $this->lb->getConnection( DB_SLAVE, 'dump' );
 286  
 287          // Discourage the server from disconnecting us if it takes a long time
 288          // to read out the big ol' batch query.
 289          $db->setSessionOptions( array( 'connTimeout' => 3600 * 24 ) );
 290  
 291          return $db;
 292      }
 293  
 294      /**
 295       * Force the dump to use the provided database connection for database
 296       * operations, wherever possible.
 297       *
 298       * @param DatabaseBase|null $db (Optional) the database connection to use. If null, resort to
 299       *   use the globally provided ways to get database connections.
 300       */
 301  	function setDb( DatabaseBase $db = null ) {
 302          $this->forcedDb = $db;
 303      }
 304  
 305  	function __destruct() {
 306          if ( isset( $this->lb ) ) {
 307              $this->lb->closeAll();
 308          }
 309      }
 310  
 311  	function backupServer() {
 312          global $wgDBserver;
 313  
 314          return $this->server
 315              ? $this->server
 316              : $wgDBserver;
 317      }
 318  
 319  	function reportPage() {
 320          $this->pageCount++;
 321      }
 322  
 323  	function revCount() {
 324          $this->revCount++;
 325          $this->report();
 326      }
 327  
 328  	function report( $final = false ) {
 329          if ( $final xor ( $this->revCount % $this->reportingInterval == 0 ) ) {
 330              $this->showReport();
 331          }
 332      }
 333  
 334  	function showReport() {
 335          if ( $this->reporting ) {
 336              $now = wfTimestamp( TS_DB );
 337              $nowts = microtime( true );
 338              $deltaAll = $nowts - $this->startTime;
 339              $deltaPart = $nowts - $this->lastTime;
 340              $this->pageCountPart = $this->pageCount - $this->pageCountLast;
 341              $this->revCountPart = $this->revCount - $this->revCountLast;
 342  
 343              if ( $deltaAll ) {
 344                  $portion = $this->revCount / $this->maxCount;
 345                  $eta = $this->startTime + $deltaAll / $portion;
 346                  $etats = wfTimestamp( TS_DB, intval( $eta ) );
 347                  $pageRate = $this->pageCount / $deltaAll;
 348                  $revRate = $this->revCount / $deltaAll;
 349              } else {
 350                  $pageRate = '-';
 351                  $revRate = '-';
 352                  $etats = '-';
 353              }
 354              if ( $deltaPart ) {
 355                  $pageRatePart = $this->pageCountPart / $deltaPart;
 356                  $revRatePart = $this->revCountPart / $deltaPart;
 357              } else {
 358                  $pageRatePart = '-';
 359                  $revRatePart = '-';
 360              }
 361              $this->progress( sprintf(
 362                  "%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), "
 363                      . "%d revs (%0.1f|%0.1f/sec all|curr), ETA %s [max %d]",
 364                  $now, wfWikiID(), $this->ID, $this->pageCount, $pageRate,
 365                  $pageRatePart, $this->revCount, $revRate, $revRatePart, $etats,
 366                  $this->maxCount
 367              ) );
 368              $this->lastTime = $nowts;
 369              $this->revCountLast = $this->revCount;
 370          }
 371      }
 372  
 373  	function progress( $string ) {
 374          fwrite( $this->stderr, $string . "\n" );
 375      }
 376  
 377  	function fatalError( $msg ) {
 378          $this->progress( "$msg\n" );
 379          die( 1 );
 380      }
 381  }
 382  
 383  class ExportProgressFilter extends DumpFilter {
 384  	function __construct( &$sink, &$progress ) {
 385          parent::__construct( $sink );
 386          $this->progress = $progress;
 387      }
 388  
 389  	function writeClosePage( $string ) {
 390          parent::writeClosePage( $string );
 391          $this->progress->reportPage();
 392      }
 393  
 394  	function writeRevision( $rev, $string ) {
 395          parent::writeRevision( $rev, $string );
 396          $this->progress->revCount();
 397      }
 398  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1