[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/installer/ -> DatabaseInstaller.php (source)

   1  <?php
   2  /**
   3   * DBMS-specific installation helper.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup Deployment
  22   */
  23  
  24  /**
  25   * Base class for DBMS-specific installation helper classes.
  26   *
  27   * @ingroup Deployment
  28   * @since 1.17
  29   */
  30  abstract class DatabaseInstaller {
  31  
  32      /**
  33       * The Installer object.
  34       *
  35       * @todo Naming this parent is confusing, 'installer' would be clearer.
  36       *
  37       * @var WebInstaller
  38       */
  39      public $parent;
  40  
  41      /**
  42       * The database connection.
  43       *
  44       * @var DatabaseBase
  45       */
  46      public $db = null;
  47  
  48      /**
  49       * Internal variables for installation.
  50       *
  51       * @var array
  52       */
  53      protected $internalDefaults = array();
  54  
  55      /**
  56       * Array of MW configuration globals this class uses.
  57       *
  58       * @var array
  59       */
  60      protected $globalNames = array();
  61  
  62      /**
  63       * Return the internal name, e.g. 'mysql', or 'sqlite'.
  64       */
  65      abstract public function getName();
  66  
  67      /**
  68       * @return bool Returns true if the client library is compiled in.
  69       */
  70      abstract public function isCompiled();
  71  
  72      /**
  73       * Checks for installation prerequisites other than those checked by isCompiled()
  74       * @since 1.19
  75       * @return Status
  76       */
  77  	public function checkPrerequisites() {
  78          return Status::newGood();
  79      }
  80  
  81      /**
  82       * Get HTML for a web form that configures this database. Configuration
  83       * at this time should be the minimum needed to connect and test
  84       * whether install or upgrade is required.
  85       *
  86       * If this is called, $this->parent can be assumed to be a WebInstaller.
  87       */
  88      abstract public function getConnectForm();
  89  
  90      /**
  91       * Set variables based on the request array, assuming it was submitted
  92       * via the form returned by getConnectForm(). Validate the connection
  93       * settings by attempting to connect with them.
  94       *
  95       * If this is called, $this->parent can be assumed to be a WebInstaller.
  96       *
  97       * @return Status
  98       */
  99      abstract public function submitConnectForm();
 100  
 101      /**
 102       * Get HTML for a web form that retrieves settings used for installation.
 103       * $this->parent can be assumed to be a WebInstaller.
 104       * If the DB type has no settings beyond those already configured with
 105       * getConnectForm(), this should return false.
 106       * @return bool
 107       */
 108  	public function getSettingsForm() {
 109          return false;
 110      }
 111  
 112      /**
 113       * Set variables based on the request array, assuming it was submitted via
 114       * the form return by getSettingsForm().
 115       *
 116       * @return Status
 117       */
 118  	public function submitSettingsForm() {
 119          return Status::newGood();
 120      }
 121  
 122      /**
 123       * Open a connection to the database using the administrative user/password
 124       * currently defined in the session, without any caching. Returns a status
 125       * object. On success, the status object will contain a Database object in
 126       * its value member.
 127       *
 128       * @return Status
 129       */
 130      abstract public function openConnection();
 131  
 132      /**
 133       * Create the database and return a Status object indicating success or
 134       * failure.
 135       *
 136       * @return Status
 137       */
 138      abstract public function setupDatabase();
 139  
 140      /**
 141       * Connect to the database using the administrative user/password currently
 142       * defined in the session. Returns a status object. On success, the status
 143       * object will contain a Database object in its value member.
 144       *
 145       * This will return a cached connection if one is available.
 146       *
 147       * @return Status
 148       */
 149  	public function getConnection() {
 150          if ( $this->db ) {
 151              return Status::newGood( $this->db );
 152          }
 153  
 154          $status = $this->openConnection();
 155          if ( $status->isOK() ) {
 156              $this->db = $status->value;
 157              // Enable autocommit
 158              $this->db->clearFlag( DBO_TRX );
 159              $this->db->commit( __METHOD__ );
 160          }
 161  
 162          return $status;
 163      }
 164  
 165      /**
 166       * Apply a SQL source file to the database as part of running an installation step.
 167       *
 168       * @param string $sourceFileMethod
 169       * @param string $stepName
 170       * @param string $archiveTableMustNotExist
 171       * @return Status
 172       */
 173  	private function stepApplySourceFile(
 174          $sourceFileMethod,
 175          $stepName,
 176          $archiveTableMustNotExist = false
 177      ) {
 178          $status = $this->getConnection();
 179          if ( !$status->isOK() ) {
 180              return $status;
 181          }
 182          $this->db->selectDB( $this->getVar( 'wgDBname' ) );
 183  
 184          if ( $archiveTableMustNotExist && $this->db->tableExists( 'archive', __METHOD__ ) ) {
 185              $status->warning( "config-$stepName-tables-exist" );
 186              $this->enableLB();
 187  
 188              return $status;
 189          }
 190  
 191          $this->db->setFlag( DBO_DDLMODE ); // For Oracle's handling of schema files
 192          $this->db->begin( __METHOD__ );
 193  
 194          $error = $this->db->sourceFile(
 195              call_user_func( array( $this->db, $sourceFileMethod ) )
 196          );
 197          if ( $error !== true ) {
 198              $this->db->reportQueryError( $error, 0, '', __METHOD__ );
 199              $this->db->rollback( __METHOD__ );
 200              $status->fatal( "config-$stepName-tables-failed", $error );
 201          } else {
 202              $this->db->commit( __METHOD__ );
 203          }
 204          // Resume normal operations
 205          if ( $status->isOk() ) {
 206              $this->enableLB();
 207          }
 208  
 209          return $status;
 210      }
 211  
 212      /**
 213       * Create database tables from scratch.
 214       *
 215       * @return Status
 216       */
 217  	public function createTables() {
 218          return $this->stepApplySourceFile( 'getSchemaPath', 'install', true );
 219      }
 220  
 221      /**
 222       * Insert update keys into table to prevent running unneded updates.
 223       *
 224       * @return Status
 225       */
 226  	public function insertUpdateKeys() {
 227          return $this->stepApplySourceFile( 'getUpdateKeysPath', 'updates', false );
 228      }
 229  
 230      /**
 231       * Create the tables for each extension the user enabled
 232       * @return Status
 233       */
 234  	public function createExtensionTables() {
 235          $status = $this->getConnection();
 236          if ( !$status->isOK() ) {
 237              return $status;
 238          }
 239  
 240          // Now run updates to create tables for old extensions
 241          DatabaseUpdater::newForDB( $this->db )->doUpdates( array( 'extensions' ) );
 242  
 243          return $status;
 244      }
 245  
 246      /**
 247       * Get the DBMS-specific options for LocalSettings.php generation.
 248       *
 249       * @return string
 250       */
 251      abstract public function getLocalSettings();
 252  
 253      /**
 254       * Override this to provide DBMS-specific schema variables, to be
 255       * substituted into tables.sql and other schema files.
 256       * @return array
 257       */
 258  	public function getSchemaVars() {
 259          return array();
 260      }
 261  
 262      /**
 263       * Set appropriate schema variables in the current database connection.
 264       *
 265       * This should be called after any request data has been imported, but before
 266       * any write operations to the database.
 267       */
 268  	public function setupSchemaVars() {
 269          $status = $this->getConnection();
 270          if ( $status->isOK() ) {
 271              $status->value->setSchemaVars( $this->getSchemaVars() );
 272          } else {
 273              $msg = __METHOD__ . ': unexpected error while establishing'
 274                  . ' a database connection with message: '
 275                  . $status->getMessage()->plain();
 276              throw new MWException( $msg );
 277          }
 278      }
 279  
 280      /**
 281       * Set up LBFactory so that wfGetDB() etc. works.
 282       * We set up a special LBFactory instance which returns the current
 283       * installer connection.
 284       */
 285  	public function enableLB() {
 286          $status = $this->getConnection();
 287          if ( !$status->isOK() ) {
 288              throw new MWException( __METHOD__ . ': unexpected DB connection error' );
 289          }
 290          LBFactory::setInstance( new LBFactorySingle( array(
 291              'connection' => $status->value ) ) );
 292      }
 293  
 294      /**
 295       * Perform database upgrades
 296       *
 297       * @return bool
 298       */
 299  	public function doUpgrade() {
 300          $this->setupSchemaVars();
 301          $this->enableLB();
 302  
 303          $ret = true;
 304          ob_start( array( $this, 'outputHandler' ) );
 305          $up = DatabaseUpdater::newForDB( $this->db );
 306          try {
 307              $up->doUpdates();
 308          } catch ( MWException $e ) {
 309              echo "\nAn error occurred:\n";
 310              echo $e->getText();
 311              $ret = false;
 312          }
 313          $up->purgeCache();
 314          ob_end_flush();
 315  
 316          return $ret;
 317      }
 318  
 319      /**
 320       * Allow DB installers a chance to make last-minute changes before installation
 321       * occurs. This happens before setupDatabase() or createTables() is called, but
 322       * long after the constructor. Helpful for things like modifying setup steps :)
 323       */
 324  	public function preInstall() {
 325      }
 326  
 327      /**
 328       * Allow DB installers a chance to make checks before upgrade.
 329       */
 330  	public function preUpgrade() {
 331      }
 332  
 333      /**
 334       * Get an array of MW configuration globals that will be configured by this class.
 335       * @return array
 336       */
 337  	public function getGlobalNames() {
 338          return $this->globalNames;
 339      }
 340  
 341      /**
 342       * Construct and initialise parent.
 343       * This is typically only called from Installer::getDBInstaller()
 344       * @param WebInstaller $parent
 345       */
 346  	public function __construct( $parent ) {
 347          $this->parent = $parent;
 348      }
 349  
 350      /**
 351       * Convenience function.
 352       * Check if a named extension is present.
 353       *
 354       * @param string $name
 355       * @return bool
 356       */
 357  	protected static function checkExtension( $name ) {
 358          return extension_loaded( $name );
 359      }
 360  
 361      /**
 362       * Get the internationalised name for this DBMS.
 363       * @return string
 364       */
 365  	public function getReadableName() {
 366          // Messages: config-type-mysql, config-type-postgres, config-type-sqlite,
 367          // config-type-oracle
 368          return wfMessage( 'config-type-' . $this->getName() )->text();
 369      }
 370  
 371      /**
 372       * Get a name=>value map of MW configuration globals that overrides.
 373       * DefaultSettings.php
 374       * @return array
 375       */
 376  	public function getGlobalDefaults() {
 377          return array();
 378      }
 379  
 380      /**
 381       * Get a name=>value map of internal variables used during installation.
 382       * @return array
 383       */
 384  	public function getInternalDefaults() {
 385          return $this->internalDefaults;
 386      }
 387  
 388      /**
 389       * Get a variable, taking local defaults into account.
 390       * @param string $var
 391       * @param mixed|null $default
 392       * @return mixed
 393       */
 394  	public function getVar( $var, $default = null ) {
 395          $defaults = $this->getGlobalDefaults();
 396          $internal = $this->getInternalDefaults();
 397          if ( isset( $defaults[$var] ) ) {
 398              $default = $defaults[$var];
 399          } elseif ( isset( $internal[$var] ) ) {
 400              $default = $internal[$var];
 401          }
 402  
 403          return $this->parent->getVar( $var, $default );
 404      }
 405  
 406      /**
 407       * Convenience alias for $this->parent->setVar()
 408       * @param string $name
 409       * @param mixed $value
 410       */
 411  	public function setVar( $name, $value ) {
 412          $this->parent->setVar( $name, $value );
 413      }
 414  
 415      /**
 416       * Get a labelled text box to configure a local variable.
 417       *
 418       * @param string $var
 419       * @param string $label
 420       * @param array $attribs
 421       * @param string $helpData
 422       * @return string
 423       */
 424  	public function getTextBox( $var, $label, $attribs = array(), $helpData = "" ) {
 425          $name = $this->getName() . '_' . $var;
 426          $value = $this->getVar( $var );
 427          if ( !isset( $attribs ) ) {
 428              $attribs = array();
 429          }
 430  
 431          return $this->parent->getTextBox( array(
 432              'var' => $var,
 433              'label' => $label,
 434              'attribs' => $attribs,
 435              'controlName' => $name,
 436              'value' => $value,
 437              'help' => $helpData
 438          ) );
 439      }
 440  
 441      /**
 442       * Get a labelled password box to configure a local variable.
 443       * Implements password hiding.
 444       *
 445       * @param string $var
 446       * @param string $label
 447       * @param array $attribs
 448       * @param string $helpData
 449       * @return string
 450       */
 451  	public function getPasswordBox( $var, $label, $attribs = array(), $helpData = "" ) {
 452          $name = $this->getName() . '_' . $var;
 453          $value = $this->getVar( $var );
 454          if ( !isset( $attribs ) ) {
 455              $attribs = array();
 456          }
 457  
 458          return $this->parent->getPasswordBox( array(
 459              'var' => $var,
 460              'label' => $label,
 461              'attribs' => $attribs,
 462              'controlName' => $name,
 463              'value' => $value,
 464              'help' => $helpData
 465          ) );
 466      }
 467  
 468      /**
 469       * Get a labelled checkbox to configure a local boolean variable.
 470       *
 471       * @param string $var
 472       * @param string $label
 473       * @param array $attribs Optional.
 474       * @param string $helpData Optional.
 475       * @return string
 476       */
 477  	public function getCheckBox( $var, $label, $attribs = array(), $helpData = "" ) {
 478          $name = $this->getName() . '_' . $var;
 479          $value = $this->getVar( $var );
 480  
 481          return $this->parent->getCheckBox( array(
 482              'var' => $var,
 483              'label' => $label,
 484              'attribs' => $attribs,
 485              'controlName' => $name,
 486              'value' => $value,
 487              'help' => $helpData
 488          ) );
 489      }
 490  
 491      /**
 492       * Get a set of labelled radio buttons.
 493       *
 494       * @param array $params Parameters are:
 495       *      var:            The variable to be configured (required)
 496       *      label:          The message name for the label (required)
 497       *      itemLabelPrefix: The message name prefix for the item labels (required)
 498       *      values:         List of allowed values (required)
 499       *      itemAttribs     Array of attribute arrays, outer key is the value name (optional)
 500       *
 501       * @return string
 502       */
 503  	public function getRadioSet( $params ) {
 504          $params['controlName'] = $this->getName() . '_' . $params['var'];
 505          $params['value'] = $this->getVar( $params['var'] );
 506  
 507          return $this->parent->getRadioSet( $params );
 508      }
 509  
 510      /**
 511       * Convenience function to set variables based on form data.
 512       * Assumes that variables containing "password" in the name are (potentially
 513       * fake) passwords.
 514       * @param array $varNames
 515       * @return array
 516       */
 517  	public function setVarsFromRequest( $varNames ) {
 518          return $this->parent->setVarsFromRequest( $varNames, $this->getName() . '_' );
 519      }
 520  
 521      /**
 522       * Determine whether an existing installation of MediaWiki is present in
 523       * the configured administrative connection. Returns true if there is
 524       * such a wiki, false if the database doesn't exist.
 525       *
 526       * Traditionally, this is done by testing for the existence of either
 527       * the revision table or the cur table.
 528       *
 529       * @return bool
 530       */
 531  	public function needsUpgrade() {
 532          $status = $this->getConnection();
 533          if ( !$status->isOK() ) {
 534              return false;
 535          }
 536  
 537          if ( !$this->db->selectDB( $this->getVar( 'wgDBname' ) ) ) {
 538              return false;
 539          }
 540  
 541          return $this->db->tableExists( 'cur', __METHOD__ ) ||
 542              $this->db->tableExists( 'revision', __METHOD__ );
 543      }
 544  
 545      /**
 546       * Get a standard install-user fieldset.
 547       *
 548       * @return string
 549       */
 550  	public function getInstallUserBox() {
 551          return Html::openElement( 'fieldset' ) .
 552              Html::element( 'legend', array(), wfMessage( 'config-db-install-account' )->text() ) .
 553              $this->getTextBox(
 554                  '_InstallUser',
 555                  'config-db-username',
 556                  array( 'dir' => 'ltr' ),
 557                  $this->parent->getHelpBox( 'config-db-install-username' )
 558              ) .
 559              $this->getPasswordBox(
 560                  '_InstallPassword',
 561                  'config-db-password',
 562                  array( 'dir' => 'ltr' ),
 563                  $this->parent->getHelpBox( 'config-db-install-password' )
 564              ) .
 565              Html::closeElement( 'fieldset' );
 566      }
 567  
 568      /**
 569       * Submit a standard install user fieldset.
 570       * @return Status
 571       */
 572  	public function submitInstallUserBox() {
 573          $this->setVarsFromRequest( array( '_InstallUser', '_InstallPassword' ) );
 574  
 575          return Status::newGood();
 576      }
 577  
 578      /**
 579       * Get a standard web-user fieldset
 580       * @param string|bool $noCreateMsg Message to display instead of the creation checkbox.
 581       *   Set this to false to show a creation checkbox (default).
 582       *
 583       * @return string
 584       */
 585  	public function getWebUserBox( $noCreateMsg = false ) {
 586          $wrapperStyle = $this->getVar( '_SameAccount' ) ? 'display: none' : '';
 587          $s = Html::openElement( 'fieldset' ) .
 588              Html::element( 'legend', array(), wfMessage( 'config-db-web-account' )->text() ) .
 589              $this->getCheckBox(
 590                  '_SameAccount', 'config-db-web-account-same',
 591                  array( 'class' => 'hideShowRadio', 'rel' => 'dbOtherAccount' )
 592              ) .
 593              Html::openElement( 'div', array( 'id' => 'dbOtherAccount', 'style' => $wrapperStyle ) ) .
 594              $this->getTextBox( 'wgDBuser', 'config-db-username' ) .
 595              $this->getPasswordBox( 'wgDBpassword', 'config-db-password' ) .
 596              $this->parent->getHelpBox( 'config-db-web-help' );
 597          if ( $noCreateMsg ) {
 598              $s .= $this->parent->getWarningBox( wfMessage( $noCreateMsg )->plain() );
 599          } else {
 600              $s .= $this->getCheckBox( '_CreateDBAccount', 'config-db-web-create' );
 601          }
 602          $s .= Html::closeElement( 'div' ) . Html::closeElement( 'fieldset' );
 603  
 604          return $s;
 605      }
 606  
 607      /**
 608       * Submit the form from getWebUserBox().
 609       *
 610       * @return Status
 611       */
 612  	public function submitWebUserBox() {
 613          $this->setVarsFromRequest(
 614              array( 'wgDBuser', 'wgDBpassword', '_SameAccount', '_CreateDBAccount' )
 615          );
 616  
 617          if ( $this->getVar( '_SameAccount' ) ) {
 618              $this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
 619              $this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) );
 620          }
 621  
 622          if ( $this->getVar( '_CreateDBAccount' ) && strval( $this->getVar( 'wgDBpassword' ) ) == '' ) {
 623              return Status::newFatal( 'config-db-password-empty', $this->getVar( 'wgDBuser' ) );
 624          }
 625  
 626          return Status::newGood();
 627      }
 628  
 629      /**
 630       * Common function for databases that don't understand the MySQLish syntax of interwiki.sql.
 631       *
 632       * @return Status
 633       */
 634  	public function populateInterwikiTable() {
 635          $status = $this->getConnection();
 636          if ( !$status->isOK() ) {
 637              return $status;
 638          }
 639          $this->db->selectDB( $this->getVar( 'wgDBname' ) );
 640  
 641          if ( $this->db->selectRow( 'interwiki', '*', array(), __METHOD__ ) ) {
 642              $status->warning( 'config-install-interwiki-exists' );
 643  
 644              return $status;
 645          }
 646          global $IP;
 647          wfSuppressWarnings();
 648          $rows = file( "$IP/maintenance/interwiki.list",
 649              FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
 650          wfRestoreWarnings();
 651          $interwikis = array();
 652          if ( !$rows ) {
 653              return Status::newFatal( 'config-install-interwiki-list' );
 654          }
 655          foreach ( $rows as $row ) {
 656              $row = preg_replace( '/^\s*([^#]*?)\s*(#.*)?$/', '\\1', $row ); // strip comments - whee
 657              if ( $row == "" ) {
 658                  continue;
 659              }
 660              $row .= "||";
 661              $interwikis[] = array_combine(
 662                  array( 'iw_prefix', 'iw_url', 'iw_local', 'iw_api', 'iw_wikiid' ),
 663                  explode( '|', $row )
 664              );
 665          }
 666          $this->db->insert( 'interwiki', $interwikis, __METHOD__ );
 667  
 668          return Status::newGood();
 669      }
 670  
 671  	public function outputHandler( $string ) {
 672          return htmlspecialchars( $string );
 673      }
 674  }


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