MediaWiki  master
Installer.php
Go to the documentation of this file.
1 <?php
32 
54 abstract class Installer {
55 
62  public const MINIMUM_PCRE_VERSION = '7.2';
63 
67  protected $settings;
68 
74  protected $compiledDBs;
75 
81  protected $dbInstallers = [];
82 
88  protected $minMemorySize = 50;
89 
95  protected $parserTitle;
96 
102  protected $parserOptions;
103 
113  protected static $dbTypes = [
114  'mysql',
115  'postgres',
116  'sqlite',
117  ];
118 
130  protected $envChecks = [
131  'envCheckDB',
132  'envCheckPCRE',
133  'envCheckMemory',
134  'envCheckCache',
135  'envCheckModSecurity',
136  'envCheckDiff3',
137  'envCheckGraphics',
138  'envCheckGit',
139  'envCheckServer',
140  'envCheckPath',
141  'envCheckShellLocale',
142  'envCheckUploadsDirectory',
143  'envCheckLibicu',
144  'envCheckSuhosinMaxValueLength',
145  'envCheck64Bit',
146  ];
147 
153  protected $envPreps = [
154  'envPrepServer',
155  'envPrepPath',
156  ];
157 
165  protected $defaultVarNames = [
166  'wgSitename',
167  'wgPasswordSender',
168  'wgLanguageCode',
169  'wgRightsIcon',
170  'wgRightsText',
171  'wgRightsUrl',
172  'wgEnableEmail',
173  'wgEnableUserEmail',
174  'wgEnotifUserTalk',
175  'wgEnotifWatchlist',
176  'wgEmailAuthentication',
177  'wgDBname',
178  'wgDBtype',
179  'wgDiff3',
180  'wgImageMagickConvertCommand',
181  'wgGitBin',
182  'IP',
183  'wgScriptPath',
184  'wgMetaNamespace',
185  'wgDeletedDirectory',
186  'wgEnableUploads',
187  'wgShellLocale',
188  'wgSecretKey',
189  'wgUseInstantCommons',
190  'wgUpgradeKey',
191  'wgDefaultSkin',
192  'wgPingback',
193  ];
194 
202  protected $internalDefaults = [
203  '_UserLang' => 'en',
204  '_Environment' => false,
205  '_RaiseMemory' => false,
206  '_UpgradeDone' => false,
207  '_InstallDone' => false,
208  '_Caches' => [],
209  '_InstallPassword' => '',
210  '_SameAccount' => true,
211  '_CreateDBAccount' => false,
212  '_NamespaceType' => 'site-name',
213  '_AdminName' => '', // will be set later, when the user selects language
214  '_AdminPassword' => '',
215  '_AdminPasswordConfirm' => '',
216  '_AdminEmail' => '',
217  '_Subscribe' => false,
218  '_SkipOptional' => 'continue',
219  '_RightsProfile' => 'wiki',
220  '_LicenseCode' => 'none',
221  '_CCDone' => false,
222  '_Extensions' => [],
223  '_Skins' => [],
224  '_MemCachedServers' => '',
225  '_UpgradeKeySupplied' => false,
226  '_ExistingDBSettings' => false,
227  // Single quotes are intentional, LocalSettingsGenerator must output this unescaped.
228  '_Logo' => '$wgResourceBasePath/resources/assets/wiki.png',
229 
230  'wgAuthenticationTokenVersion' => 1,
231  ];
232 
239  private $installSteps = [];
240 
246  protected $extraInstallSteps = [];
247 
253  protected $objectCaches = [
254  'apcu' => 'apcu_fetch',
255  'wincache' => 'wincache_ucache_get'
256  ];
257 
263  public $rightsProfiles = [
264  'wiki' => [],
265  'no-anon' => [
266  '*' => [ 'edit' => false ]
267  ],
268  'fishbowl' => [
269  '*' => [
270  'createaccount' => false,
271  'edit' => false,
272  ],
273  ],
274  'private' => [
275  '*' => [
276  'createaccount' => false,
277  'edit' => false,
278  'read' => false,
279  ],
280  ],
281  ];
282 
288  public $licenses = [
289  'cc-by' => [
290  'url' => 'https://creativecommons.org/licenses/by/4.0/',
291  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by.png',
292  ],
293  'cc-by-sa' => [
294  'url' => 'https://creativecommons.org/licenses/by-sa/4.0/',
295  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-sa.png',
296  ],
297  'cc-by-nc-sa' => [
298  'url' => 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
299  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-nc-sa.png',
300  ],
301  'cc-0' => [
302  'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
303  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-0.png',
304  ],
305  'gfdl' => [
306  'url' => 'https://www.gnu.org/copyleft/fdl.html',
307  'icon' => '$wgResourceBasePath/resources/assets/licenses/gnu-fdl.png',
308  ],
309  'none' => [
310  'url' => '',
311  'icon' => '',
312  'text' => ''
313  ],
314  'cc-choose' => [
315  // Details will be filled in by the selector.
316  'url' => '',
317  'icon' => '',
318  'text' => '',
319  ],
320  ];
321 
326  'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
327 
332  'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
333  'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
334  'sl', 'sr', 'sv', 'tr', 'uk'
335  ];
336 
341 
350  abstract public function showMessage( $msg, ...$params );
351 
357  abstract public function showError( $msg, ...$params );
358 
363  abstract public function showStatusMessage( Status $status );
364 
375  public static function getInstallerConfig( Config $baseConfig ) {
376  $configOverrides = new HashConfig();
377 
378  // disable (problematic) object cache types explicitly, preserving all other (working) ones
379  // bug T113843
380  $emptyCache = [ 'class' => EmptyBagOStuff::class ];
381 
382  $objectCaches = [
383  CACHE_NONE => $emptyCache,
384  CACHE_DB => $emptyCache,
385  CACHE_ANYTHING => $emptyCache,
386  CACHE_MEMCACHED => $emptyCache,
387  ] + $baseConfig->get( 'ObjectCaches' );
388 
389  $configOverrides->set( 'ObjectCaches', $objectCaches );
390 
391  // Load the installer's i18n.
392  $messageDirs = $baseConfig->get( 'MessagesDirs' );
393  $messageDirs['MediawikiInstaller'] = __DIR__ . '/i18n';
394 
395  $configOverrides->set( 'MessagesDirs', $messageDirs );
396 
397  $installerConfig = new MultiConfig( [ $configOverrides, $baseConfig ] );
398 
399  // make sure we use the installer config as the main config
400  $configRegistry = $baseConfig->get( 'ConfigRegistry' );
401  $configRegistry['main'] = static function () use ( $installerConfig ) {
402  return $installerConfig;
403  };
404 
405  $configOverrides->set( 'ConfigRegistry', $configRegistry );
406 
407  return $installerConfig;
408  }
409 
413  public function __construct() {
414  $defaultConfig = new GlobalVarConfig(); // all the stuff from DefaultSettings.php
415  $installerConfig = self::getInstallerConfig( $defaultConfig );
416 
417  $this->resetMediaWikiServices( $installerConfig );
418 
419  // Disable all storage services, since we don't have any configuration yet!
420  MediaWikiServices::disableStorageBackend();
421 
422  $this->settings = $this->internalDefaults;
423 
424  foreach ( $this->defaultVarNames as $var ) {
425  $this->settings[$var] = $GLOBALS[$var];
426  }
427 
428  $this->doEnvironmentPreps();
429 
430  $this->compiledDBs = [];
431  foreach ( self::getDBTypes() as $type ) {
432  $installer = $this->getDBInstaller( $type );
433 
434  if ( !$installer->isCompiled() ) {
435  continue;
436  }
437  $this->compiledDBs[] = $type;
438  }
439 
440  $this->parserTitle = Title::newFromText( 'Installer' );
441  }
442 
457  public function resetMediaWikiServices( Config $installerConfig = null, $serviceOverrides = [] ) {
458  global $wgUser, $wgObjectCaches, $wgLang;
459 
460  $serviceOverrides += [
461  // Disable interwiki lookup, to avoid database access during parses
462  'InterwikiLookup' => static function () {
463  return new NullInterwikiLookup();
464  },
465 
466  // Disable user options database fetching, only rely on default options.
467  'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
468  return $services->get( '_DefaultOptionsLookup' );
469  }
470  ];
471 
472  $lang = $this->getVar( '_UserLang', 'en' );
473 
474  // Reset all services and inject config overrides
475  MediaWikiServices::resetGlobalInstance( $installerConfig );
476 
477  $mwServices = MediaWikiServices::getInstance();
478 
479  foreach ( $serviceOverrides as $name => $callback ) {
480  // Skip if the caller set $callback to null
481  // to suppress default overrides.
482  if ( $callback ) {
483  $mwServices->redefineService( $name, $callback );
484  }
485  }
486 
487  // Disable i18n cache
488  $mwServices->getLocalisationCache()->disableBackend();
489 
490  // Clear language cache so the old i18n cache doesn't sneak back in
492 
493  // Set a fake user.
494  // Note that this will reset the context's language,
495  // so set the user before setting the language.
496  $user = User::newFromId( 0 );
497  $wgUser = $user;
498 
499  RequestContext::getMain()->setUser( $user );
500 
501  // Don't attempt to load user language options (T126177)
502  // This will be overridden in the web installer with the user-specified language
503  // Ensure $wgLang does not have a reference to a stale LocalisationCache instance
504  // (T241638, T261081)
505  RequestContext::getMain()->setLanguage( $lang );
506  $wgLang = RequestContext::getMain()->getLanguage();
507 
508  // Disable object cache (otherwise CACHE_ANYTHING will try CACHE_DB and
509  // SqlBagOStuff will then throw since we just disabled wfGetDB)
510  $wgObjectCaches = $mwServices->getMainConfig()->get( 'ObjectCaches' );
511 
512  $this->parserOptions = new ParserOptions( $user ); // language will be wrong :(
513  // Don't try to access DB before user language is initialised
514  $this->setParserLanguage( $mwServices->getLanguageFactory()->getLanguage( 'en' ) );
515 
516  return $mwServices;
517  }
518 
524  public static function getDBTypes() {
525  return self::$dbTypes;
526  }
527 
541  public function doEnvironmentChecks() {
542  // PHP version has already been checked by entry scripts
543  // Show message here for information purposes
544  $this->showMessage( 'config-env-php', PHP_VERSION );
545 
546  $good = true;
547  // Must go here because an old version of PCRE can prevent other checks from completing
548  $pcreVersion = explode( ' ', PCRE_VERSION, 2 )[0];
549  if ( version_compare( $pcreVersion, self::MINIMUM_PCRE_VERSION, '<' ) ) {
550  $this->showError( 'config-pcre-old', self::MINIMUM_PCRE_VERSION, $pcreVersion );
551  $good = false;
552  } else {
553  foreach ( $this->envChecks as $check ) {
554  $status = $this->$check();
555  if ( $status === false ) {
556  $good = false;
557  }
558  }
559  }
560 
561  $this->setVar( '_Environment', $good );
562 
563  return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' );
564  }
565 
566  public function doEnvironmentPreps() {
567  foreach ( $this->envPreps as $prep ) {
568  $this->$prep();
569  }
570  }
571 
578  public function setVar( $name, $value ) {
579  $this->settings[$name] = $value;
580  }
581 
592  public function getVar( $name, $default = null ) {
593  return $this->settings[$name] ?? $default;
594  }
595 
601  public function getCompiledDBs() {
602  return $this->compiledDBs;
603  }
604 
612  public static function getDBInstallerClass( $type ) {
613  return ucfirst( $type ) . 'Installer';
614  }
615 
623  public function getDBInstaller( $type = false ) {
624  if ( !$type ) {
625  $type = $this->getVar( 'wgDBtype' );
626  }
627 
628  $type = strtolower( $type );
629 
630  if ( !isset( $this->dbInstallers[$type] ) ) {
631  $class = self::getDBInstallerClass( $type );
632  $this->dbInstallers[$type] = new $class( $this );
633  }
634 
635  return $this->dbInstallers[$type];
636  }
637 
643  public static function getExistingLocalSettings() {
644  global $IP;
645 
646  // You might be wondering why this is here. Well if you don't do this
647  // then some poorly-formed extensions try to call their own classes
648  // after immediately registering them. We really need to get extension
649  // registration out of the global scope and into a real format.
650  // @see https://phabricator.wikimedia.org/T69440
651  global $wgAutoloadClasses;
652  $wgAutoloadClasses = [];
653 
654  // LocalSettings.php should not call functions, except wfLoadSkin/wfLoadExtensions
655  // Define the required globals here, to ensure, the functions can do it work correctly.
656  // phpcs:ignore MediaWiki.VariableAnalysis.UnusedGlobalVariables
658 
659  Wikimedia\suppressWarnings();
660  $_lsExists = file_exists( "$IP/LocalSettings.php" );
661  Wikimedia\restoreWarnings();
662 
663  if ( !$_lsExists ) {
664  return false;
665  }
666  unset( $_lsExists );
667 
668  require "$IP/includes/DefaultSettings.php";
669  require "$IP/LocalSettings.php";
670 
671  return get_defined_vars();
672  }
673 
683  public function getFakePassword( $realPassword ) {
684  return str_repeat( '*', strlen( $realPassword ) );
685  }
686 
694  public function setPassword( $name, $value ) {
695  if ( !preg_match( '/^\*+$/', $value ) ) {
696  $this->setVar( $name, $value );
697  }
698  }
699 
711  public static function maybeGetWebserverPrimaryGroup() {
712  if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
713  # I don't know this, this isn't UNIX.
714  return null;
715  }
716 
717  # posix_getegid() *not* getmygid() because we want the group of the webserver,
718  # not whoever owns the current script.
719  $gid = posix_getegid();
720  return posix_getpwuid( $gid )['name'] ?? null;
721  }
722 
739  public function parse( $text, $lineStart = false ) {
740  $parser = MediaWikiServices::getInstance()->getParser();
741 
742  try {
743  $out = $parser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
744  $html = $out->getText( [
745  'enableSectionEditLinks' => false,
746  'unwrap' => true,
747  ] );
748  $html = Parser::stripOuterParagraph( $html );
749  } catch ( Wikimedia\Services\ServiceDisabledException $e ) {
750  $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
751  }
752 
753  return $html;
754  }
755 
759  public function getParserOptions() {
760  return $this->parserOptions;
761  }
762 
763  public function disableLinkPopups() {
764  $this->parserOptions->setExternalLinkTarget( false );
765  }
766 
767  public function restoreLinkPopups() {
768  global $wgExternalLinkTarget;
769  $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget );
770  }
771 
780  public function populateSiteStats( DatabaseInstaller $installer ) {
781  $status = $installer->getConnection();
782  if ( !$status->isOK() ) {
783  return $status;
784  }
785  // @phan-suppress-next-line PhanUndeclaredMethod
786  $status->value->insert(
787  'site_stats',
788  [
789  'ss_row_id' => 1,
790  'ss_total_edits' => 0,
791  'ss_good_articles' => 0,
792  'ss_total_pages' => 0,
793  'ss_users' => 0,
794  'ss_active_users' => 0,
795  'ss_images' => 0
796  ],
797  __METHOD__,
798  'IGNORE'
799  );
800 
801  return Status::newGood();
802  }
803 
808  protected function envCheckDB() {
809  global $wgLang;
811  $dbType = $this->getVar( 'wgDBtype' );
812 
813  $allNames = [];
814 
815  // Messages: config-type-mysql, config-type-postgres, config-type-sqlite
816  foreach ( self::getDBTypes() as $name ) {
817  $allNames[] = wfMessage( "config-type-$name" )->text();
818  }
819 
820  $databases = $this->getCompiledDBs();
821 
822  $databases = array_flip( $databases );
823  $ok = true;
824  foreach ( array_keys( $databases ) as $db ) {
825  $installer = $this->getDBInstaller( $db );
826  $status = $installer->checkPrerequisites();
827  if ( !$status->isGood() ) {
828  if ( !$this instanceof WebInstaller && $db === $dbType ) {
829  // Strictly check the key database type instead of just outputting message
830  // Note: No perform this check run from the web installer, since this method always called by
831  // the welcome page under web installation, so $dbType will always be 'mysql'
832  $ok = false;
833  }
834  $this->showStatusMessage( $status );
835  unset( $databases[$db] );
836  }
837  }
838  $databases = array_flip( $databases );
839  if ( !$databases ) {
840  $this->showError( 'config-no-db', $wgLang->commaList( $allNames ), count( $allNames ) );
841  return false;
842  }
843  return $ok;
844  }
845 
854  protected function envCheckPCRE() {
855  Wikimedia\suppressWarnings();
856  $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' );
857  // Need to check for \p support too, as PCRE can be compiled
858  // with utf8 support, but not unicode property support.
859  // check that \p{Zs} (space separators) matches
860  // U+3000 (Ideographic space)
861  $regexprop = preg_replace( '/\p{Zs}/u', '', "-\u{3000}-" );
862  Wikimedia\restoreWarnings();
863  if ( $regexd != '--' || $regexprop != '--' ) {
864  $this->showError( 'config-pcre-no-utf8' );
865 
866  return false;
867  }
868 
869  return true;
870  }
871 
876  protected function envCheckMemory() {
877  $limit = ini_get( 'memory_limit' );
878 
879  if ( !$limit || $limit == -1 ) {
880  return true;
881  }
882 
883  $n = wfShorthandToInteger( $limit );
884 
885  if ( $n < $this->minMemorySize * 1024 * 1024 ) {
886  $newLimit = "{$this->minMemorySize}M";
887 
888  if ( ini_set( "memory_limit", $newLimit ) === false ) {
889  $this->showMessage( 'config-memory-bad', $limit );
890  } else {
891  $this->showMessage( 'config-memory-raised', $limit, $newLimit );
892  $this->setVar( '_RaiseMemory', true );
893  }
894  }
895 
896  return true;
897  }
898 
902  protected function envCheckCache() {
903  $caches = [];
904  foreach ( $this->objectCaches as $name => $function ) {
905  if ( function_exists( $function ) ) {
906  $caches[$name] = true;
907  }
908  }
909 
910  if ( !$caches ) {
911  $this->showMessage( 'config-no-cache-apcu' );
912  }
913 
914  $this->setVar( '_Caches', $caches );
915  }
916 
921  protected function envCheckModSecurity() {
922  if ( self::apacheModulePresent( 'mod_security' )
923  || self::apacheModulePresent( 'mod_security2' ) ) {
924  $this->showMessage( 'config-mod-security' );
925  }
926 
927  return true;
928  }
929 
934  protected function envCheckDiff3() {
935  $names = [ "gdiff3", "diff3" ];
936  if ( wfIsWindows() ) {
937  $names[] = 'diff3.exe';
938  }
939  $versionInfo = [ '--version', 'GNU diffutils' ];
940 
941  $diff3 = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
942 
943  if ( $diff3 ) {
944  $this->setVar( 'wgDiff3', $diff3 );
945  } else {
946  $this->setVar( 'wgDiff3', false );
947  $this->showMessage( 'config-diff3-bad' );
948  }
949 
950  return true;
951  }
952 
957  protected function envCheckGraphics() {
958  $names = wfIsWindows() ? 'convert.exe' : 'convert';
959  $versionInfo = [ '-version', 'ImageMagick' ];
960  $convert = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
961 
962  $this->setVar( 'wgImageMagickConvertCommand', '' );
963  if ( $convert ) {
964  $this->setVar( 'wgImageMagickConvertCommand', $convert );
965  $this->showMessage( 'config-imagemagick', $convert );
966  } elseif ( function_exists( 'imagejpeg' ) ) {
967  $this->showMessage( 'config-gd' );
968  } else {
969  $this->showMessage( 'config-no-scaling' );
970  }
971 
972  return true;
973  }
974 
981  protected function envCheckGit() {
982  $names = wfIsWindows() ? 'git.exe' : 'git';
983  $versionInfo = [ '--version', 'git version' ];
984 
985  $git = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
986 
987  if ( $git ) {
988  $this->setVar( 'wgGitBin', $git );
989  $this->showMessage( 'config-git', $git );
990  } else {
991  $this->setVar( 'wgGitBin', false );
992  $this->showMessage( 'config-git-bad' );
993  }
994 
995  return true;
996  }
997 
1003  protected function envCheckServer() {
1004  $server = $this->envGetDefaultServer();
1005  if ( $server !== null ) {
1006  $this->showMessage( 'config-using-server', $server );
1007  }
1008  return true;
1009  }
1010 
1016  protected function envCheckPath() {
1017  $this->showMessage(
1018  'config-using-uri',
1019  $this->getVar( 'wgServer' ),
1020  $this->getVar( 'wgScriptPath' )
1021  );
1022  return true;
1023  }
1024 
1029  protected function envCheckShellLocale() {
1030  $os = php_uname( 's' );
1031  $supported = [ 'Linux', 'SunOS', 'HP-UX', 'Darwin' ]; # Tested these
1032 
1033  if ( !in_array( $os, $supported ) ) {
1034  return true;
1035  }
1036 
1037  if ( Shell::isDisabled() ) {
1038  return true;
1039  }
1040 
1041  # Get a list of available locales.
1042  $result = Shell::command( '/usr/bin/locale', '-a' )->execute();
1043 
1044  if ( $result->getExitCode() != 0 ) {
1045  return true;
1046  }
1047 
1048  $lines = $result->getStdout();
1049  $lines = array_map( 'trim', explode( "\n", $lines ) );
1050  $candidatesByLocale = [];
1051  $candidatesByLang = [];
1052  foreach ( $lines as $line ) {
1053  if ( $line === '' ) {
1054  continue;
1055  }
1056 
1057  if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) {
1058  continue;
1059  }
1060 
1061  list( , $lang, , , ) = $m;
1062 
1063  $candidatesByLocale[$m[0]] = $m;
1064  $candidatesByLang[$lang][] = $m;
1065  }
1066 
1067  # Try the current value of LANG.
1068  if ( isset( $candidatesByLocale[getenv( 'LANG' )] ) ) {
1069  $this->setVar( 'wgShellLocale', getenv( 'LANG' ) );
1070 
1071  return true;
1072  }
1073 
1074  # Try the most common ones.
1075  $commonLocales = [ 'C.UTF-8', 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' ];
1076  foreach ( $commonLocales as $commonLocale ) {
1077  if ( isset( $candidatesByLocale[$commonLocale] ) ) {
1078  $this->setVar( 'wgShellLocale', $commonLocale );
1079 
1080  return true;
1081  }
1082  }
1083 
1084  # Is there an available locale in the Wiki's language?
1085  $wikiLang = $this->getVar( 'wgLanguageCode' );
1086 
1087  if ( isset( $candidatesByLang[$wikiLang] ) ) {
1088  $m = reset( $candidatesByLang[$wikiLang] );
1089  $this->setVar( 'wgShellLocale', $m[0] );
1090 
1091  return true;
1092  }
1093 
1094  # Are there any at all?
1095  if ( count( $candidatesByLocale ) ) {
1096  $m = reset( $candidatesByLocale );
1097  $this->setVar( 'wgShellLocale', $m[0] );
1098 
1099  return true;
1100  }
1101 
1102  # Give up.
1103  return true;
1104  }
1105 
1110  protected function envCheckUploadsDirectory() {
1111  global $IP;
1112 
1113  $dir = $IP . '/images/';
1114  $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/';
1115  $safe = !$this->dirIsExecutable( $dir, $url );
1116 
1117  if ( !$safe ) {
1118  $this->showMessage( 'config-uploads-not-safe', $dir );
1119  }
1120 
1121  return true;
1122  }
1123 
1129  protected function envCheckSuhosinMaxValueLength() {
1130  $currentValue = ini_get( 'suhosin.get.max_value_length' );
1131  $minRequired = 2000;
1132  $recommended = 5000;
1133  if ( $currentValue > 0 && $currentValue < $minRequired ) {
1134  $this->showError( 'config-suhosin-max-value-length', $currentValue, $minRequired, $recommended );
1135  return false;
1136  }
1137 
1138  return true;
1139  }
1140 
1147  protected function envCheck64Bit() {
1148  if ( PHP_INT_SIZE == 4 ) {
1149  $this->showMessage( 'config-using-32bit' );
1150  }
1151 
1152  return true;
1153  }
1154 
1158  protected function envCheckLibicu() {
1166  $not_normal_c = "\u{FA6C}";
1167  $normal_c = "\u{242EE}";
1168 
1169  $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C );
1170 
1171  $this->showMessage( 'config-unicode-using-intl' );
1172  if ( $intl !== $normal_c ) {
1173  $this->showMessage( 'config-unicode-update-warning' );
1174  }
1175  }
1176 
1180  protected function envPrepServer() {
1181  $server = $this->envGetDefaultServer();
1182  if ( $server !== null ) {
1183  $this->setVar( 'wgServer', $server );
1184  }
1185  }
1186 
1191  abstract protected function envGetDefaultServer();
1192 
1196  protected function envPrepPath() {
1197  global $IP;
1198  $IP = dirname( dirname( __DIR__ ) );
1199  $this->setVar( 'IP', $IP );
1200  }
1201 
1210  public function dirIsExecutable( $dir, $url ) {
1211  $scriptTypes = [
1212  'php' => [
1213  "<?php echo 'exec';",
1214  "#!/var/env php\n<?php echo 'exec';",
1215  ],
1216  ];
1217 
1218  // it would be good to check other popular languages here, but it'll be slow.
1219  // TODO no need to have a loop if there is going to only be one script type
1220 
1221  $httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
1222 
1223  Wikimedia\suppressWarnings();
1224 
1225  foreach ( $scriptTypes as $ext => $contents ) {
1226  foreach ( $contents as $source ) {
1227  $file = 'exectest.' . $ext;
1228 
1229  if ( !file_put_contents( $dir . $file, $source ) ) {
1230  break;
1231  }
1232 
1233  try {
1234  $text = $httpRequestFactory->get(
1235  $url . $file,
1236  [ 'timeout' => 3 ],
1237  __METHOD__
1238  );
1239  } catch ( Exception $e ) {
1240  // HttpRequestFactory::get can throw with allow_url_fopen = false and no curl
1241  // extension.
1242  $text = null;
1243  }
1244  unlink( $dir . $file );
1245 
1246  if ( $text == 'exec' ) {
1247  Wikimedia\restoreWarnings();
1248 
1249  return $ext;
1250  }
1251  }
1252  }
1253 
1254  Wikimedia\restoreWarnings();
1255 
1256  return false;
1257  }
1258 
1265  public static function apacheModulePresent( $moduleName ) {
1266  if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) {
1267  return true;
1268  }
1269  // try it the hard way
1270  ob_start();
1271  phpinfo( INFO_MODULES );
1272  $info = ob_get_clean();
1273 
1274  return strpos( $info, $moduleName ) !== false;
1275  }
1276 
1282  public function setParserLanguage( $lang ) {
1283  $this->parserOptions->setTargetLanguage( $lang );
1284  $this->parserOptions->setUserLang( $lang );
1285  }
1286 
1292  protected function getDocUrl( $page ) {
1293  return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
1294  }
1295 
1305  public function findExtensions( $directory = 'extensions' ) {
1306  switch ( $directory ) {
1307  case 'extensions':
1308  return $this->findExtensionsByType( 'extension', 'extensions' );
1309  case 'skins':
1310  return $this->findExtensionsByType( 'skin', 'skins' );
1311  default:
1312  throw new InvalidArgumentException( "Invalid extension type" );
1313  }
1314  }
1315 
1325  protected function findExtensionsByType( $type = 'extension', $directory = 'extensions' ) {
1326  if ( $this->getVar( 'IP' ) === null ) {
1327  return Status::newGood( [] );
1328  }
1329 
1330  $extDir = $this->getVar( 'IP' ) . '/' . $directory;
1331  if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) {
1332  return Status::newGood( [] );
1333  }
1334 
1335  // @phan-suppress-next-line SecurityCheck-PathTraversal False positive T268920
1336  $dh = opendir( $extDir );
1337  $exts = [];
1338  $status = new Status;
1339  while ( ( $file = readdir( $dh ) ) !== false ) {
1340  // skip non-dirs and hidden directories
1341  if ( !is_dir( "$extDir/$file" ) || $file[0] === '.' ) {
1342  continue;
1343  }
1344  $extStatus = $this->getExtensionInfo( $type, $directory, $file );
1345  if ( $extStatus->isOK() ) {
1346  $exts[$file] = $extStatus->value;
1347  } elseif ( $extStatus->hasMessage( 'config-extension-not-found' ) ) {
1348  // (T225512) The directory is not actually an extension. Downgrade to warning.
1349  $status->warning( 'config-extension-not-found', $file );
1350  } else {
1351  $status->merge( $extStatus );
1352  }
1353  }
1354  closedir( $dh );
1355  uksort( $exts, 'strnatcasecmp' );
1356 
1357  $status->value = $exts;
1358 
1359  return $status;
1360  }
1361 
1369  protected function getExtensionInfo( $type, $parentRelPath, $name ) {
1370  if ( $this->getVar( 'IP' ) === null ) {
1371  throw new Exception( 'Cannot find extensions since the IP variable is not yet set' );
1372  }
1373  if ( $type !== 'extension' && $type !== 'skin' ) {
1374  throw new InvalidArgumentException( "Invalid extension type" );
1375  }
1376  $absDir = $this->getVar( 'IP' ) . "/$parentRelPath/$name";
1377  $relDir = "../$parentRelPath/$name";
1378  if ( !is_dir( $absDir ) ) {
1379  return Status::newFatal( 'config-extension-not-found', $name );
1380  }
1381  $jsonFile = $type . '.json';
1382  $fullJsonFile = "$absDir/$jsonFile";
1383  $isJson = file_exists( $fullJsonFile );
1384  $isPhp = false;
1385  if ( !$isJson ) {
1386  // Only fallback to PHP file if JSON doesn't exist
1387  $fullPhpFile = "$absDir/$name.php";
1388  $isPhp = file_exists( $fullPhpFile );
1389  }
1390  if ( !$isJson && !$isPhp ) {
1391  return Status::newFatal( 'config-extension-not-found', $name );
1392  }
1393 
1394  // Extension exists. Now see if there are screenshots
1395  $info = [];
1396  if ( is_dir( "$absDir/screenshots" ) ) {
1397  $paths = glob( "$absDir/screenshots/*.png" );
1398  foreach ( $paths as $path ) {
1399  $info['screenshots'][] = str_replace( $absDir, $relDir, $path );
1400  }
1401  }
1402 
1403  if ( $isJson ) {
1404  $jsonStatus = $this->readExtension( $fullJsonFile );
1405  if ( !$jsonStatus->isOK() ) {
1406  return $jsonStatus;
1407  }
1408  $info += $jsonStatus->value;
1409  }
1410 
1411  return Status::newGood( $info );
1412  }
1413 
1422  private function readExtension( $fullJsonFile, $extDeps = [], $skinDeps = [] ) {
1423  $load = [
1424  $fullJsonFile => 1
1425  ];
1426  if ( $extDeps ) {
1427  $extDir = $this->getVar( 'IP' ) . '/extensions';
1428  foreach ( $extDeps as $dep ) {
1429  $fname = "$extDir/$dep/extension.json";
1430  if ( !file_exists( $fname ) ) {
1431  return Status::newFatal( 'config-extension-not-found', $dep );
1432  }
1433  $load[$fname] = 1;
1434  }
1435  }
1436  if ( $skinDeps ) {
1437  $skinDir = $this->getVar( 'IP' ) . '/skins';
1438  foreach ( $skinDeps as $dep ) {
1439  $fname = "$skinDir/$dep/skin.json";
1440  if ( !file_exists( $fname ) ) {
1441  return Status::newFatal( 'config-extension-not-found', $dep );
1442  }
1443  $load[$fname] = 1;
1444  }
1445  }
1446  $registry = new ExtensionRegistry();
1447  try {
1448  $info = $registry->readFromQueue( $load );
1449  } catch ( ExtensionDependencyError $e ) {
1450  if ( $e->incompatibleCore || $e->incompatibleSkins
1451  || $e->incompatibleExtensions
1452  ) {
1453  // If something is incompatible with a dependency, we have no real
1454  // option besides skipping it
1455  return Status::newFatal( 'config-extension-dependency',
1456  basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1457  } elseif ( $e->missingExtensions || $e->missingSkins ) {
1458  // There's an extension missing in the dependency tree,
1459  // so add those to the dependency list and try again
1460  $status = $this->readExtension(
1461  $fullJsonFile,
1462  array_merge( $extDeps, $e->missingExtensions ),
1463  array_merge( $skinDeps, $e->missingSkins )
1464  );
1465  if ( !$status->isOK() && !$status->hasMessage( 'config-extension-dependency' ) ) {
1466  $status = Status::newFatal( 'config-extension-dependency',
1467  basename( dirname( $fullJsonFile ) ), $status->getMessage() );
1468  }
1469  return $status;
1470  }
1471  // Some other kind of dependency error?
1472  return Status::newFatal( 'config-extension-dependency',
1473  basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1474  }
1475  $ret = [];
1476  // The order of credits will be the order of $load,
1477  // so the first extension is the one we want to load,
1478  // everything else is a dependency
1479  $i = 0;
1480  foreach ( $info['credits'] as $name => $credit ) {
1481  $i++;
1482  if ( $i == 1 ) {
1483  // Extension we want to load
1484  continue;
1485  }
1486  $type = basename( $credit['path'] ) === 'skin.json' ? 'skins' : 'extensions';
1487  $ret['requires'][$type][] = $credit['name'];
1488  }
1489  $credits = array_values( $info['credits'] )[0];
1490  if ( isset( $credits['url'] ) ) {
1491  $ret['url'] = $credits['url'];
1492  }
1493  $ret['type'] = $credits['type'];
1494 
1495  return Status::newGood( $ret );
1496  }
1497 
1506  public function getDefaultSkin( array $skinNames ) {
1507  $defaultSkin = $GLOBALS['wgDefaultSkin'];
1508  if ( !$skinNames || in_array( $defaultSkin, $skinNames ) ) {
1509  return $defaultSkin;
1510  } else {
1511  return $skinNames[0];
1512  }
1513  }
1514 
1520  protected function includeExtensions() {
1521  // Marker for DatabaseUpdater::loadExtensions so we don't
1522  // double load extensions
1523  define( 'MW_EXTENSIONS_LOADED', true );
1524 
1525  $legacySchemaHooks = $this->getAutoExtensionLegacyHooks();
1526  $data = $this->getAutoExtensionData();
1527  if ( isset( $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] ) ) {
1528  $legacySchemaHooks = array_merge( $legacySchemaHooks,
1529  $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] );
1530  }
1531  $extDeprecatedHooks = $data['attributes']['DeprecatedHooks'] ?? [];
1532 
1533  $this->autoExtensionHookContainer = new HookContainer(
1534  new StaticHookRegistry(
1535  [ 'LoadExtensionSchemaUpdates' => $legacySchemaHooks ],
1536  $data['attributes']['Hooks'] ?? [],
1537  $extDeprecatedHooks
1538  ),
1539  MediaWikiServices::getInstance()->getObjectFactory()
1540  );
1541 
1542  return Status::newGood();
1543  }
1544 
1552  protected function getAutoExtensionLegacyHooks() {
1553  $exts = $this->getVar( '_Extensions' );
1554  $installPath = $this->getVar( 'IP' );
1555  $files = [];
1556  foreach ( $exts as $e ) {
1557  if ( file_exists( "$installPath/extensions/$e/$e.php" ) ) {
1558  $files[] = "$installPath/extensions/$e/$e.php";
1559  }
1560  }
1561 
1562  if ( $files ) {
1563  return $this->includeExtensionFiles( $files );
1564  } else {
1565  return [];
1566  }
1567  }
1568 
1576  protected function includeExtensionFiles( $files ) {
1577  global $IP;
1578  $IP = $this->getVar( 'IP' );
1579 
1588  require "$IP/includes/DefaultSettings.php";
1589 
1590  // phpcs:ignore MediaWiki.VariableAnalysis.UnusedGlobalVariables
1591  global $wgAutoloadClasses;
1592  foreach ( $files as $file ) {
1593  require_once $file;
1594  }
1595 
1596  // @phpcs:disable MediaWiki.VariableAnalysis.MisleadingGlobalNames.Misleading$wgHooks
1597  // @phan-suppress-next-line PhanUndeclaredVariable,PhanCoalescingAlwaysNull $wgHooks is set by DefaultSettings
1598  $hooksWeWant = $wgHooks['LoadExtensionSchemaUpdates'] ?? [];
1599  // @phpcs:enable MediaWiki.VariableAnalysis.MisleadingGlobalNames.Misleading$wgHooks
1600 
1601  // Ignore everyone else's hooks. Lord knows what someone might be doing
1602  // in ParserFirstCallInit (see T29171)
1603  return [ 'LoadExtensionSchemaUpdates' => $hooksWeWant ];
1604  }
1605 
1612  protected function getAutoExtensionData() {
1613  $exts = $this->getVar( '_Extensions' );
1614  $installPath = $this->getVar( 'IP' );
1615  $queue = [];
1616  foreach ( $exts as $e ) {
1617  if ( file_exists( "$installPath/extensions/$e/extension.json" ) ) {
1618  $queue["$installPath/extensions/$e/extension.json"] = 1;
1619  }
1620  }
1621 
1622  $registry = new ExtensionRegistry();
1623  $data = $registry->readFromQueue( $queue );
1624  global $wgAutoloadClasses;
1625  $wgAutoloadClasses += $data['globals']['wgAutoloadClasses'];
1626  return $data;
1627  }
1628 
1636  public function getAutoExtensionHookContainer() {
1637  if ( !$this->autoExtensionHookContainer ) {
1638  throw new \Exception( __METHOD__ .
1639  ': includeExtensions() has not been called' );
1640  }
1642  }
1643 
1657  protected function getInstallSteps( DatabaseInstaller $installer ) {
1658  $coreInstallSteps = [
1659  [ 'name' => 'database', 'callback' => [ $installer, 'setupDatabase' ] ],
1660  [ 'name' => 'tables', 'callback' => [ $installer, 'createTables' ] ],
1661  [ 'name' => 'tables-manual', 'callback' => [ $installer, 'createManualTables' ] ],
1662  [ 'name' => 'interwiki', 'callback' => [ $installer, 'populateInterwikiTable' ] ],
1663  [ 'name' => 'stats', 'callback' => [ $this, 'populateSiteStats' ] ],
1664  [ 'name' => 'keys', 'callback' => [ $this, 'generateKeys' ] ],
1665  [ 'name' => 'updates', 'callback' => [ $installer, 'insertUpdateKeys' ] ],
1666  [ 'name' => 'restore-services', 'callback' => [ $this, 'restoreServices' ] ],
1667  [ 'name' => 'sysop', 'callback' => [ $this, 'createSysop' ] ],
1668  [ 'name' => 'mainpage', 'callback' => [ $this, 'createMainpage' ] ],
1669  ];
1670 
1671  // Build the array of install steps starting from the core install list,
1672  // then adding any callbacks that wanted to attach after a given step
1673  foreach ( $coreInstallSteps as $step ) {
1674  $this->installSteps[] = $step;
1675  if ( isset( $this->extraInstallSteps[$step['name']] ) ) {
1676  $this->installSteps = array_merge(
1677  $this->installSteps,
1678  $this->extraInstallSteps[$step['name']]
1679  );
1680  }
1681  }
1682 
1683  // Prepend any steps that want to be at the beginning
1684  if ( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
1685  $this->installSteps = array_merge(
1686  $this->extraInstallSteps['BEGINNING'],
1687  $this->installSteps
1688  );
1689  }
1690 
1691  // Extensions should always go first, chance to tie into hooks and such
1692  if ( count( $this->getVar( '_Extensions' ) ) ) {
1693  array_unshift( $this->installSteps,
1694  [ 'name' => 'extensions', 'callback' => [ $this, 'includeExtensions' ] ]
1695  );
1696  $this->installSteps[] = [
1697  'name' => 'extension-tables',
1698  'callback' => [ $installer, 'createExtensionTables' ]
1699  ];
1700  }
1701 
1702  return $this->installSteps;
1703  }
1704 
1713  public function performInstallation( $startCB, $endCB ) {
1714  $installResults = [];
1715  $installer = $this->getDBInstaller();
1716  $installer->preInstall();
1717  $steps = $this->getInstallSteps( $installer );
1718  foreach ( $steps as $stepObj ) {
1719  $name = $stepObj['name'];
1720  call_user_func_array( $startCB, [ $name ] );
1721 
1722  // Perform the callback step
1723  $status = call_user_func( $stepObj['callback'], $installer );
1724 
1725  // Output and save the results
1726  call_user_func( $endCB, $name, $status );
1727  $installResults[$name] = $status;
1728 
1729  // If we've hit some sort of fatal, we need to bail.
1730  // Callback already had a chance to do output above.
1731  if ( !$status->isOK() ) {
1732  break;
1733  }
1734  }
1735  if ( $status->isOK() ) {
1736  $this->showMessage(
1737  'config-install-db-success'
1738  );
1739  $this->setVar( '_InstallDone', true );
1740  }
1741 
1742  return $installResults;
1743  }
1744 
1750  public function generateKeys() {
1751  $keys = [ 'wgSecretKey' => 64 ];
1752  if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
1753  $keys['wgUpgradeKey'] = 16;
1754  }
1755 
1756  return $this->doGenerateKeys( $keys );
1757  }
1758 
1763  public function restoreServices() {
1764  $this->resetMediaWikiServices( null, [
1765  'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
1766  return $services->get( 'UserOptionsManager' );
1767  }
1768  ] );
1769  return Status::newGood();
1770  }
1771 
1778  protected function doGenerateKeys( $keys ) {
1779  foreach ( $keys as $name => $length ) {
1780  $secretKey = MWCryptRand::generateHex( $length );
1781  $this->setVar( $name, $secretKey );
1782  }
1783  return Status::newGood();
1784  }
1785 
1791  protected function createSysop() {
1792  $name = $this->getVar( '_AdminName' );
1793  $user = User::newFromName( $name );
1794 
1795  if ( !$user ) {
1796  // We should've validated this earlier anyway!
1797  return Status::newFatal( 'config-admin-error-user', $name );
1798  }
1799 
1800  if ( $user->idForName() == 0 ) {
1801  $user->addToDatabase();
1802 
1803  $password = $this->getVar( '_AdminPassword' );
1804  $status = $user->changeAuthenticationData( [
1805  'username' => $user->getName(),
1806  'password' => $password,
1807  'retype' => $password,
1808  ] );
1809  if ( !$status->isGood() ) {
1810  return Status::newFatal( 'config-admin-error-password',
1811  $name, $status->getWikiText( null, null, $this->getVar( '_UserLang' ) ) );
1812  }
1813 
1814  $user->addGroup( 'sysop' );
1815  $user->addGroup( 'bureaucrat' );
1816  $user->addGroup( 'interface-admin' );
1817  if ( $this->getVar( '_AdminEmail' ) ) {
1818  $user->setEmail( $this->getVar( '_AdminEmail' ) );
1819  }
1820  $user->saveSettings();
1821 
1822  // Update user count
1823  $ssUpdate = SiteStatsUpdate::factory( [ 'users' => 1 ] );
1824  $ssUpdate->doUpdate();
1825  }
1826 
1827  if ( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
1828  return $this->subscribeToMediaWikiAnnounce();
1829  }
1830  return Status::newGood();
1831  }
1832 
1836  private function subscribeToMediaWikiAnnounce() {
1837  $params = [
1838  'email' => $this->getVar( '_AdminEmail' ),
1839  'language' => 'en',
1840  'digest' => 0
1841  ];
1842 
1843  // Mailman doesn't support as many languages as we do, so check to make
1844  // sure their selected language is available
1845  $myLang = $this->getVar( '_UserLang' );
1846  if ( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
1847  $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
1848  $params['language'] = $myLang;
1849  }
1850 
1851  $status = Status::newGood();
1854  $this->mediaWikiAnnounceUrl,
1855  [ 'method' => 'POST', 'postData' => $params ],
1856  __METHOD__
1857  )->execute();
1858  if ( !$res->isOK() ) {
1859  $status->warning( 'config-install-subscribe-fail', $res->getMessage() );
1860  }
1861  } else {
1862  $status->warning( 'config-install-subscribe-notpossible' );
1863  }
1864  return $status;
1865  }
1866 
1873  protected function createMainpage( DatabaseInstaller $installer ) {
1874  $status = Status::newGood();
1876  if ( $title->exists() ) {
1877  $status->warning( 'config-install-mainpage-exists' );
1878  return $status;
1879  }
1880  try {
1881  $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title );
1882  $content = new WikitextContent(
1883  wfMessage( 'mainpagetext' )->inContentLanguage()->text() . "\n\n" .
1884  wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text()
1885  );
1886 
1887  $status = $page->doEditContent(
1888  $content,
1889  '',
1890  EDIT_NEW,
1891  false,
1892  User::newSystemUser( 'MediaWiki default' )
1893  );
1894  } catch ( Exception $e ) {
1895  // using raw, because $wgShowExceptionDetails can not be set yet
1896  $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
1897  }
1898 
1899  return $status;
1900  }
1901 
1905  public static function overrideConfig() {
1906  // Use PHP's built-in session handling, since MediaWiki's
1907  // SessionHandler can't work before we have an object cache set up.
1908  if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) {
1909  define( 'MW_NO_SESSION_HANDLER', 1 );
1910  }
1911 
1912  // Don't access the database
1913  $GLOBALS['wgUseDatabaseMessages'] = false;
1914  // Don't cache langconv tables
1915  $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE;
1916  // Don't try to cache ResourceLoader dependencies in the database
1917  $GLOBALS['wgResourceLoaderUseObjectCacheForDeps'] = true;
1918  // Debug-friendly
1919  $GLOBALS['wgShowExceptionDetails'] = true;
1920  $GLOBALS['wgShowHostnames'] = true;
1921  // Don't break forms
1922  $GLOBALS['wgExternalLinkTarget'] = '_blank';
1923 
1924  // Allow multiple ob_flush() calls
1925  $GLOBALS['wgDisableOutputCompression'] = true;
1926 
1927  // Use a sensible cookie prefix (not my_wiki)
1928  $GLOBALS['wgCookiePrefix'] = 'mw_installer';
1929 
1930  // Some of the environment checks make shell requests, remove limits
1931  $GLOBALS['wgMaxShellMemory'] = 0;
1932 
1933  // Override the default CookieSessionProvider with a dummy
1934  // implementation that won't stomp on PHP's cookies.
1935  $GLOBALS['wgSessionProviders'] = [
1936  [
1937  'class' => InstallerSessionProvider::class,
1938  'args' => [ [
1939  'priority' => 1,
1940  ] ]
1941  ]
1942  ];
1943 
1944  // Don't use the DB as the main stash
1945  $GLOBALS['wgMainStash'] = CACHE_NONE;
1946 
1947  // Don't try to use any object cache for SessionManager either.
1948  $GLOBALS['wgSessionCacheType'] = CACHE_NONE;
1949 
1950  // Set a dummy $wgServer to bypass the check in Setup.php, the
1951  // web installer will automatically detect it and not use this value.
1952  $GLOBALS['wgServer'] = 'https://🌻.invalid';
1953  }
1954 
1962  public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
1963  $this->extraInstallSteps[$findStep][] = $callback;
1964  }
1965 
1970  protected function disableTimeLimit() {
1971  Wikimedia\suppressWarnings();
1972  set_time_limit( 0 );
1973  Wikimedia\restoreWarnings();
1974  }
1975 }
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:44
MediaWiki\Shell\Shell
Executes shell commands.
Definition: Shell.php:45
Installer\__construct
__construct()
Constructor, always call this from child classes.
Definition: Installer.php:413
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:627
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:363
Installer\getAutoExtensionData
getAutoExtensionData()
Auto-detect extensions with an extension.json file.
Definition: Installer.php:1612
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
CACHE_ANYTHING
const CACHE_ANYTHING
Definition: Defines.php:85
Installer\createMainpage
createMainpage(DatabaseInstaller $installer)
Insert Main Page with default content.
Definition: Installer.php:1873
Installer\subscribeToMediaWikiAnnounce
subscribeToMediaWikiAnnounce()
Definition: Installer.php:1836
Installer\parse
parse( $text, $lineStart=false)
Convert wikitext $text to HTML.
Definition: Installer.php:739
ExtensionDependencyError
Copyright (C) 2018 Kunal Mehta legoktm@member.fsf.org
Definition: ExtensionDependencyError.php:25
MultiConfig
Provides a fallback sequence for Config objects.
Definition: MultiConfig.php:28
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:173
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
HashConfig
A Config instance which stores all settings as a member variable.
Definition: HashConfig.php:28
Installer\showStatusMessage
showStatusMessage(Status $status)
Show a message to the installing user by using a Status object.
Installer\dirIsExecutable
dirIsExecutable( $dir, $url)
Checks if scripts located in the given directory can be executed via the given URL.
Definition: Installer.php:1210
Installer\envCheckLibicu
envCheckLibicu()
Check the libicu version.
Definition: Installer.php:1158
ExtensionRegistry
ExtensionRegistry class.
Definition: ExtensionRegistry.php:17
Installer\populateSiteStats
populateSiteStats(DatabaseInstaller $installer)
Install step which adds a row to the site_stats table with appropriate initial values.
Definition: Installer.php:780
MediaWiki\HookContainer\StaticHookRegistry
This is a simple immutable HookRegistry which can be used to set up a local HookContainer in tests an...
Definition: StaticHookRegistry.php:9
WebInstaller
Class for the core installer web interface.
Definition: WebInstaller.php:32
Installer\$extraInstallSteps
array $extraInstallSteps
Extra steps for installation, for things like DatabaseInstallers to modify.
Definition: Installer.php:246
DatabaseInstaller\getConnection
getConnection()
Connect to the database using the administrative user/password currently defined in the session.
Definition: DatabaseInstaller.php:186
Installer\$rightsProfiles
array $rightsProfiles
User rights profiles.
Definition: Installer.php:263
Installer\envCheckShellLocale
envCheckShellLocale()
Environment check for preferred locale in shell.
Definition: Installer.php:1029
Installer\envCheckUploadsDirectory
envCheckUploadsDirectory()
Environment check for the permissions of the uploads directory.
Definition: Installer.php:1110
Installer\$autoExtensionHookContainer
HookContainer null $autoExtensionHookContainer
Definition: Installer.php:340
Installer\$settings
array $settings
Definition: Installer.php:67
Installer\envPrepServer
envPrepServer()
Environment prep for the server hostname.
Definition: Installer.php:1180
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
Installer\performInstallation
performInstallation( $startCB, $endCB)
Actually perform the installation.
Definition: Installer.php:1713
Installer\$mediaWikiAnnounceLanguages
$mediaWikiAnnounceLanguages
Supported language codes for Mailman.
Definition: Installer.php:331
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:586
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1231
MediaWiki\Interwiki\NullInterwikiLookup
An interwiki lookup that has no data, intended for use in the installer.
Definition: NullInterwikiLookup.php:29
Installer\$dbInstallers
array $dbInstallers
Cached DB installer instances, access using getDBInstaller().
Definition: Installer.php:81
Installer\setParserLanguage
setParserLanguage( $lang)
ParserOptions are constructed before we determined the language, so fix it.
Definition: Installer.php:1282
Title\newMainPage
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:688
Installer\addInstallStep
addInstallStep( $callback, $findStep='BEGINNING')
Add an installation step following the given step.
Definition: Installer.php:1962
Installer\$internalDefaults
array $internalDefaults
Variables that are stored alongside globals, and are used for any configuration of the installation p...
Definition: Installer.php:202
$res
$res
Definition: testCompression.php:57
Installer\setPassword
setPassword( $name, $value)
Set a variable which stores a password, except if the new value is a fake password in which case leav...
Definition: Installer.php:694
Installer\envGetDefaultServer
envGetDefaultServer()
Helper function to be called from envPrepServer()
$wgLang
$wgLang
Definition: Setup.php:782
Installer\setVar
setVar( $name, $value)
Set a MW configuration variable, or internal installer configuration variable.
Definition: Installer.php:578
Installer\restoreServices
restoreServices()
Restore services that have been redefined in the early stage of installation.
Definition: Installer.php:1763
Installer\$parserTitle
Title $parserTitle
Cached Title, used by parse().
Definition: Installer.php:95
Installer\$objectCaches
array $objectCaches
Known object cache types and the functions used to test for their existence.
Definition: Installer.php:253
Installer\$minMemorySize
int $minMemorySize
Minimum memory size in MB.
Definition: Installer.php:88
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
User\newSystemUser
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition: User.php:789
Installer\overrideConfig
static overrideConfig()
Override the necessary bits of the config to run an installation.
Definition: Installer.php:1905
Installer\createSysop
createSysop()
Create the first user account, grant it sysop, bureaucrat and interface-admin rights.
Definition: Installer.php:1791
Installer\getFakePassword
getFakePassword( $realPassword)
Get a fake password for sending back to the user in HTML.
Definition: Installer.php:683
Config
Interface for configuration instances.
Definition: Config.php:30
Installer\showMessage
showMessage( $msg,... $params)
UI interface for displaying a short message The parameters are like parameters to wfMessage().
Installer\envCheckSuhosinMaxValueLength
envCheckSuhosinMaxValueLength()
Checks if suhosin.get.max_value_length is set, and if so generate a warning because it is incompatibl...
Definition: Installer.php:1129
$wgHooks
$wgHooks
Global list of hooks.
Definition: DefaultSettings.php:8021
Installer\envPrepPath
envPrepPath()
Environment prep for setting $IP and $wgScriptPath.
Definition: Installer.php:1196
Installer\includeExtensionFiles
includeExtensionFiles( $files)
Include the specified extension PHP files.
Definition: Installer.php:1576
Installer\envCheckDiff3
envCheckDiff3()
Search for GNU diff3.
Definition: Installer.php:934
Config\get
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Installer\generateKeys
generateKeys()
Generate $wgSecretKey.
Definition: Installer.php:1750
CACHE_MEMCACHED
const CACHE_MEMCACHED
Definition: Defines.php:88
Installer\envCheckMemory
envCheckMemory()
Environment check for available memory.
Definition: Installer.php:876
$wgObjectCaches
$wgObjectCaches
Advanced object cache configuration.
Definition: DefaultSettings.php:2577
Installer\getCompiledDBs
getCompiledDBs()
Get a list of DBs supported by current PHP setup.
Definition: Installer.php:601
$queue
$queue
Definition: mergeMessageFileList.php:176
Installer\envCheckCache
envCheckCache()
Environment check for compiled object cache types.
Definition: Installer.php:902
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:136
Installer\getExistingLocalSettings
static getExistingLocalSettings()
Determine if LocalSettings.php exists.
Definition: Installer.php:643
Installer\envCheckPath
envCheckPath()
Environment check to inform user which paths we've assumed.
Definition: Installer.php:1016
Installer\doGenerateKeys
doGenerateKeys( $keys)
Generate a secret value for variables using a secure generator.
Definition: Installer.php:1778
Installer\$parserOptions
ParserOptions $parserOptions
Cached ParserOptions, used by parse().
Definition: Installer.php:102
$title
$title
Definition: testCompression.php:38
SiteStatsUpdate\factory
static factory(array $deltas)
Definition: SiteStatsUpdate.php:71
GlobalVarConfig
Accesses configuration settings from $GLOBALS.
Definition: GlobalVarConfig.php:29
$wgExternalLinkTarget
$wgExternalLinkTarget
Set a default target for external links, e.g.
Definition: DefaultSettings.php:4769
Installer\$dbTypes
static array $dbTypes
Known database types.
Definition: Installer.php:113
Installer\getVar
getVar( $name, $default=null)
Get an MW configuration variable, or internal installer configuration variable.
Definition: Installer.php:592
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:37
Installer\restoreLinkPopups
restoreLinkPopups()
Definition: Installer.php:767
Installer\getDefaultSkin
getDefaultSkin(array $skinNames)
Returns a default value to be used for $wgDefaultSkin: normally the one set in DefaultSettings,...
Definition: Installer.php:1506
Installer\findExtensions
findExtensions( $directory='extensions')
Find extensions or skins in a subdirectory of $IP.
Definition: Installer.php:1305
Installer\getInstallerConfig
static getInstallerConfig(Config $baseConfig)
Constructs a Config object that contains configuration settings that should be overwritten for the in...
Definition: Installer.php:375
$content
$content
Definition: router.php:76
Installer\$envPreps
array $envPreps
A list of environment preparation methods called by doEnvironmentPreps().
Definition: Installer.php:153
$wgExtensionDirectory
$wgExtensionDirectory
Filesystem extensions directory.
Definition: DefaultSettings.php:248
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
CACHE_DB
const CACHE_DB
Definition: Defines.php:87
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:1870
$line
$line
Definition: mcc.php:119
Installer\doEnvironmentPreps
doEnvironmentPreps()
Definition: Installer.php:566
Installer\resetMediaWikiServices
resetMediaWikiServices(Config $installerConfig=null, $serviceOverrides=[])
Reset the global service container and associated global state to accommodate different stages of the...
Definition: Installer.php:457
Installer\readExtension
readExtension( $fullJsonFile, $extDeps=[], $skinDeps=[])
Definition: Installer.php:1422
Installer\getDBInstaller
getDBInstaller( $type=false)
Get an instance of DatabaseInstaller for the specified DB type.
Definition: Installer.php:623
DatabaseInstaller
Base class for DBMS-specific installation helper classes.
Definition: DatabaseInstaller.php:37
Installer\envCheckDB
envCheckDB()
Environment check for DB types.
Definition: Installer.php:808
Installer\getInstallSteps
getInstallSteps(DatabaseInstaller $installer)
Get an array of install steps.
Definition: Installer.php:1657
MWCryptRand\generateHex
static generateHex( $chars)
Generate a run of cryptographically random data and return it in hexadecimal string format.
Definition: MWCryptRand.php:36
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:476
$wgAutoloadClasses
$wgAutoloadClasses
Array mapping class names to filenames, for autoloading.
Definition: DefaultSettings.php:7915
Installer\$compiledDBs
array $compiledDBs
List of detected DBs, access using getCompiledDBs().
Definition: Installer.php:74
Installer\getAutoExtensionHookContainer
getAutoExtensionHookContainer()
Get the hook container previously populated by includeExtensions().
Definition: Installer.php:1636
$lines
if(!file_exists( $CREDITS)) $lines
Definition: updateCredits.php:45
Installer\doEnvironmentChecks
doEnvironmentChecks()
Do initial checks of the PHP environment.
Definition: Installer.php:541
Language\$mLangObjCache
static array $mLangObjCache
Definition: Language.php:146
Installer\envCheckGraphics
envCheckGraphics()
Environment check for ImageMagick and GD.
Definition: Installer.php:957
wfShorthandToInteger
wfShorthandToInteger( $string='', $default=-1)
Converts shorthand byte notation to integer form.
Definition: GlobalFunctions.php:2672
Installer\apacheModulePresent
static apacheModulePresent( $moduleName)
Checks for presence of an Apache module.
Definition: Installer.php:1265
Title
Represents a title within MediaWiki.
Definition: Title.php:46
Installer\envCheckPCRE
envCheckPCRE()
Environment check for the PCRE module.
Definition: Installer.php:854
Parser\stripOuterParagraph
static stripOuterParagraph( $html)
Strip outer.
Definition: Parser.php:6304
Installer\getParserOptions
getParserOptions()
Definition: Installer.php:759
MWHttpRequest\canMakeRequests
static canMakeRequests()
Simple function to test if we can make any sort of requests at all, using cURL or fopen()
Definition: MWHttpRequest.php:181
$path
$path
Definition: NoLocalSettings.php:25
Installer\envCheck64Bit
envCheck64Bit()
Checks if we're running on 64 bit or not.
Definition: Installer.php:1147
Installer\$licenses
array $licenses
License types.
Definition: Installer.php:288
Installer\disableTimeLimit
disableTimeLimit()
Disable the time limit for execution.
Definition: Installer.php:1970
Wikimedia
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...
$keys
$keys
Definition: testCompression.php:72
$source
$source
Definition: mwdoc-filter.php:34
Installer\envCheckServer
envCheckServer()
Environment check to inform user which server we've assumed.
Definition: Installer.php:1003
Installer
Base installer class.
Definition: Installer.php:54
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
Installer\MINIMUM_PCRE_VERSION
const MINIMUM_PCRE_VERSION
The oldest version of PCRE we can support.
Definition: Installer.php:62
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
Installer\findExtensionsByType
findExtensionsByType( $type='extension', $directory='extensions')
Find extensions or skins, and return an array containing the value for 'Name' for each found extensio...
Definition: Installer.php:1325
Installer\showError
showError( $msg,... $params)
Same as showMessage(), but for displaying errors.
ExecutableFinder\findInDefaultPaths
static findInDefaultPaths( $names, $versionInfo=false)
Same as locateExecutable(), but checks in getPossibleBinPaths() by default.
Definition: ExecutableFinder.php:96
Installer\getDBTypes
static getDBTypes()
Get a list of known DB types.
Definition: Installer.php:524
Installer\disableLinkPopups
disableLinkPopups()
Definition: Installer.php:763
Installer\getAutoExtensionLegacyHooks
getAutoExtensionLegacyHooks()
Auto-detect extensions with an old style .php registration file, load the extensions,...
Definition: Installer.php:1552
Installer\$installSteps
array[] $installSteps
The actual list of installation steps.
Definition: Installer.php:239
Installer\$mediaWikiAnnounceUrl
$mediaWikiAnnounceUrl
URL to mediawiki-announce subscription.
Definition: Installer.php:325
$IP
$IP
Definition: WebStart.php:49
Installer\includeExtensions
includeExtensions()
Installs the auto-detected extensions.
Definition: Installer.php:1520
$wgStyleDirectory
$wgStyleDirectory
Filesystem stylesheets directory.
Definition: DefaultSettings.php:255
Installer\envCheckGit
envCheckGit()
Search for git.
Definition: Installer.php:981
Installer\getDocUrl
getDocUrl( $page)
Overridden by WebInstaller to provide lastPage parameters.
Definition: Installer.php:1292
MWHttpRequest\factory
static factory( $url, array $options=null, $caller=__METHOD__)
Generate a new request object.
Definition: MWHttpRequest.php:195
Installer\maybeGetWebserverPrimaryGroup
static maybeGetWebserverPrimaryGroup()
On POSIX systems return the primary group of the webserver we're running under.
Definition: Installer.php:711
Installer\getExtensionInfo
getExtensionInfo( $type, $parentRelPath, $name)
Definition: Installer.php:1369
Installer\$defaultVarNames
array $defaultVarNames
MediaWiki configuration globals that will eventually be passed through to LocalSettings....
Definition: Installer.php:165
Installer\getDBInstallerClass
static getDBInstallerClass( $type)
Get the DatabaseInstaller class name for this type.
Definition: Installer.php:612
CACHE_NONE
const CACHE_NONE
Definition: Defines.php:86
Installer\$envChecks
array $envChecks
A list of environment check methods called by doEnvironmentChecks().
Definition: Installer.php:130
$type
$type
Definition: testCompression.php:52
Installer\envCheckModSecurity
envCheckModSecurity()
Scare user to death if they have mod_security or mod_security2.
Definition: Installer.php:921