MediaWiki  master
Installer.php
Go to the documentation of this file.
1 <?php
31 
53 abstract class Installer {
54 
61  public const MINIMUM_PCRE_VERSION = '7.2';
62 
66  private const MEDIAWIKI_ANNOUNCE_URL =
67  'https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/';
68 
72  protected $settings;
73 
79  protected $compiledDBs;
80 
86  protected $dbInstallers = [];
87 
93  protected $minMemorySize = 50;
94 
100  protected $parserTitle;
101 
107  protected $parserOptions;
108 
118  protected static $dbTypes = [
119  'mysql',
120  'postgres',
121  'sqlite',
122  ];
123 
135  protected $envChecks = [
136  'envCheckDB',
137  'envCheckPCRE',
138  'envCheckMemory',
139  'envCheckCache',
140  'envCheckModSecurity',
141  'envCheckDiff3',
142  'envCheckGraphics',
143  'envCheckGit',
144  'envCheckServer',
145  'envCheckPath',
146  'envCheckUploadsDirectory',
147  'envCheckLibicu',
148  'envCheckSuhosinMaxValueLength',
149  'envCheck64Bit',
150  ];
151 
157  protected $envPreps = [
158  'envPrepServer',
159  'envPrepPath',
160  ];
161 
169  protected $defaultVarNames = [
170  'wgSitename',
171  'wgPasswordSender',
172  'wgLanguageCode',
173  'wgLocaltimezone',
174  'wgRightsIcon',
175  'wgRightsText',
176  'wgRightsUrl',
177  'wgEnableEmail',
178  'wgEnableUserEmail',
179  'wgEnotifUserTalk',
180  'wgEnotifWatchlist',
181  'wgEmailAuthentication',
182  'wgDBname',
183  'wgDBtype',
184  'wgDiff3',
185  'wgImageMagickConvertCommand',
186  'wgGitBin',
187  'IP',
188  'wgScriptPath',
189  'wgMetaNamespace',
190  'wgDeletedDirectory',
191  'wgEnableUploads',
192  'wgSecretKey',
193  'wgUseInstantCommons',
194  'wgUpgradeKey',
195  'wgDefaultSkin',
196  'wgPingback',
197  ];
198 
206  protected $internalDefaults = [
207  '_UserLang' => 'en',
208  '_Environment' => false,
209  '_RaiseMemory' => false,
210  '_UpgradeDone' => false,
211  '_InstallDone' => false,
212  '_Caches' => [],
213  '_InstallPassword' => '',
214  '_SameAccount' => true,
215  '_CreateDBAccount' => false,
216  '_NamespaceType' => 'site-name',
217  '_AdminName' => '', // will be set later, when the user selects language
218  '_AdminPassword' => '',
219  '_AdminPasswordConfirm' => '',
220  '_AdminEmail' => '',
221  '_Subscribe' => false,
222  '_SkipOptional' => 'continue',
223  '_RightsProfile' => 'wiki',
224  '_LicenseCode' => 'none',
225  '_CCDone' => false,
226  '_Extensions' => [],
227  '_Skins' => [],
228  '_MemCachedServers' => '',
229  '_UpgradeKeySupplied' => false,
230  '_ExistingDBSettings' => false,
231  // Single quotes are intentional, LocalSettingsGenerator must output this unescaped.
232  '_Logo' => '$wgResourceBasePath/resources/assets/wiki.png',
233 
234  'wgAuthenticationTokenVersion' => 1,
235  ];
236 
243  private $installSteps = [];
244 
250  protected $extraInstallSteps = [];
251 
257  protected $objectCaches = [
258  'apcu' => 'apcu_fetch',
259  'wincache' => 'wincache_ucache_get'
260  ];
261 
267  public $rightsProfiles = [
268  'wiki' => [],
269  'no-anon' => [
270  '*' => [ 'edit' => false ]
271  ],
272  'fishbowl' => [
273  '*' => [
274  'createaccount' => false,
275  'edit' => false,
276  ],
277  ],
278  'private' => [
279  '*' => [
280  'createaccount' => false,
281  'edit' => false,
282  'read' => false,
283  ],
284  ],
285  ];
286 
292  public $licenses = [
293  'cc-by' => [
294  'url' => 'https://creativecommons.org/licenses/by/4.0/',
295  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by.png',
296  ],
297  'cc-by-sa' => [
298  'url' => 'https://creativecommons.org/licenses/by-sa/4.0/',
299  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-sa.png',
300  ],
301  'cc-by-nc-sa' => [
302  'url' => 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
303  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-nc-sa.png',
304  ],
305  'cc-0' => [
306  'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
307  'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-0.png',
308  ],
309  'gfdl' => [
310  'url' => 'https://www.gnu.org/copyleft/fdl.html',
311  'icon' => '$wgResourceBasePath/resources/assets/licenses/gnu-fdl.png',
312  ],
313  'none' => [
314  'url' => '',
315  'icon' => '',
316  'text' => ''
317  ],
318  'cc-choose' => [
319  // Details will be filled in by the selector.
320  'url' => '',
321  'icon' => '',
322  'text' => '',
323  ],
324  ];
325 
330 
339  abstract public function showMessage( $msg, ...$params );
340 
346  abstract public function showError( $msg, ...$params );
347 
352  abstract public function showStatusMessage( Status $status );
353 
364  public static function getInstallerConfig( Config $baseConfig ) {
365  $configOverrides = new HashConfig();
366 
367  // disable (problematic) object cache types explicitly, preserving all other (working) ones
368  // bug T113843
369  $emptyCache = [ 'class' => EmptyBagOStuff::class ];
370 
371  $objectCaches = [
372  CACHE_NONE => $emptyCache,
373  CACHE_DB => $emptyCache,
374  CACHE_ANYTHING => $emptyCache,
375  CACHE_MEMCACHED => $emptyCache,
376  ] + $baseConfig->get( 'ObjectCaches' );
377 
378  $configOverrides->set( 'ObjectCaches', $objectCaches );
379 
380  // Load the installer's i18n.
381  $messageDirs = $baseConfig->get( 'MessagesDirs' );
382  $messageDirs['MediawikiInstaller'] = __DIR__ . '/i18n';
383 
384  $configOverrides->set( 'MessagesDirs', $messageDirs );
385 
386  $installerConfig = new MultiConfig( [ $configOverrides, $baseConfig ] );
387 
388  // make sure we use the installer config as the main config
389  $configRegistry = $baseConfig->get( 'ConfigRegistry' );
390  $configRegistry['main'] = static function () use ( $installerConfig ) {
391  return $installerConfig;
392  };
393 
394  $configOverrides->set( 'ConfigRegistry', $configRegistry );
395 
396  return $installerConfig;
397  }
398 
402  public function __construct() {
403  $defaultConfig = new GlobalVarConfig(); // all the stuff from DefaultSettings.php
404  $installerConfig = self::getInstallerConfig( $defaultConfig );
405 
406  $this->resetMediaWikiServices( $installerConfig );
407 
408  // Disable all storage services, since we don't have any configuration yet!
409  MediaWikiServices::disableStorageBackend();
410 
411  $this->settings = $this->internalDefaults;
412 
413  foreach ( $this->defaultVarNames as $var ) {
414  $this->settings[$var] = $GLOBALS[$var];
415  }
416 
417  $this->doEnvironmentPreps();
418 
419  $this->compiledDBs = [];
420  foreach ( self::getDBTypes() as $type ) {
421  $installer = $this->getDBInstaller( $type );
422 
423  if ( !$installer->isCompiled() ) {
424  continue;
425  }
426  $this->compiledDBs[] = $type;
427  }
428 
429  $this->parserTitle = Title::newFromText( 'Installer' );
430  }
431 
446  public function resetMediaWikiServices( Config $installerConfig = null, $serviceOverrides = [] ) {
447  global $wgObjectCaches, $wgLang;
448 
449  $serviceOverrides += [
450  // Disable interwiki lookup, to avoid database access during parses
451  'InterwikiLookup' => static function () {
452  return new NullInterwikiLookup();
453  },
454 
455  // Disable user options database fetching, only rely on default options.
456  'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
457  return $services->get( '_DefaultOptionsLookup' );
458  }
459  ];
460 
461  $lang = $this->getVar( '_UserLang', 'en' );
462 
463  // Reset all services and inject config overrides
464  MediaWikiServices::resetGlobalInstance( $installerConfig );
465 
466  $mwServices = MediaWikiServices::getInstance();
467 
468  foreach ( $serviceOverrides as $name => $callback ) {
469  // Skip if the caller set $callback to null
470  // to suppress default overrides.
471  if ( $callback ) {
472  $mwServices->redefineService( $name, $callback );
473  }
474  }
475 
476  // Disable i18n cache
477  $mwServices->getLocalisationCache()->disableBackend();
478 
479  // Set a fake user.
480  // Note that this will reset the context's language,
481  // so set the user before setting the language.
482  $user = User::newFromId( 0 );
483  StubGlobalUser::setUser( $user );
484 
485  RequestContext::getMain()->setUser( $user );
486 
487  // Don't attempt to load user language options (T126177)
488  // This will be overridden in the web installer with the user-specified language
489  // Ensure $wgLang does not have a reference to a stale LocalisationCache instance
490  // (T241638, T261081)
491  RequestContext::getMain()->setLanguage( $lang );
492  $wgLang = RequestContext::getMain()->getLanguage();
493 
494  // Disable object cache (otherwise CACHE_ANYTHING will try CACHE_DB and
495  // SqlBagOStuff will then throw since we just disabled wfGetDB)
496  $wgObjectCaches = $mwServices->getMainConfig()->get( 'ObjectCaches' );
497 
498  $this->parserOptions = new ParserOptions( $user ); // language will be wrong :(
499  // Don't try to access DB before user language is initialised
500  $this->setParserLanguage( $mwServices->getLanguageFactory()->getLanguage( 'en' ) );
501 
502  return $mwServices;
503  }
504 
510  public static function getDBTypes() {
511  return self::$dbTypes;
512  }
513 
527  public function doEnvironmentChecks() {
528  // PHP version has already been checked by entry scripts
529  // Show message here for information purposes
530  $this->showMessage( 'config-env-php', PHP_VERSION );
531 
532  $good = true;
533  // Must go here because an old version of PCRE can prevent other checks from completing
534  $pcreVersion = explode( ' ', PCRE_VERSION, 2 )[0];
535  if ( version_compare( $pcreVersion, self::MINIMUM_PCRE_VERSION, '<' ) ) {
536  $this->showError( 'config-pcre-old', self::MINIMUM_PCRE_VERSION, $pcreVersion );
537  $good = false;
538  } else {
539  foreach ( $this->envChecks as $check ) {
540  $status = $this->$check();
541  if ( $status === false ) {
542  $good = false;
543  }
544  }
545  }
546 
547  $this->setVar( '_Environment', $good );
548 
549  return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' );
550  }
551 
552  public function doEnvironmentPreps() {
553  foreach ( $this->envPreps as $prep ) {
554  $this->$prep();
555  }
556  }
557 
564  public function setVar( $name, $value ) {
565  $this->settings[$name] = $value;
566  }
567 
578  public function getVar( $name, $default = null ) {
579  return $this->settings[$name] ?? $default;
580  }
581 
587  public function getCompiledDBs() {
588  return $this->compiledDBs;
589  }
590 
598  public static function getDBInstallerClass( $type ) {
599  return ucfirst( $type ) . 'Installer';
600  }
601 
609  public function getDBInstaller( $type = false ) {
610  if ( !$type ) {
611  $type = $this->getVar( 'wgDBtype' );
612  }
613 
614  $type = strtolower( $type );
615 
616  if ( !isset( $this->dbInstallers[$type] ) ) {
617  $class = self::getDBInstallerClass( $type );
618  $this->dbInstallers[$type] = new $class( $this );
619  }
620 
621  return $this->dbInstallers[$type];
622  }
623 
629  public static function getExistingLocalSettings() {
630  global $IP;
631 
632  // You might be wondering why this is here. Well if you don't do this
633  // then some poorly-formed extensions try to call their own classes
634  // after immediately registering them. We really need to get extension
635  // registration out of the global scope and into a real format.
636  // @see https://phabricator.wikimedia.org/T69440
637  global $wgAutoloadClasses;
638  $wgAutoloadClasses = [];
639 
640  // LocalSettings.php should not call functions, except wfLoadSkin/wfLoadExtensions
641  // Define the required globals here, to ensure, the functions can do it work correctly.
642  // phpcs:ignore MediaWiki.VariableAnalysis.UnusedGlobalVariables
644 
645  Wikimedia\suppressWarnings();
646  $_lsExists = file_exists( "$IP/LocalSettings.php" );
647  Wikimedia\restoreWarnings();
648 
649  if ( !$_lsExists ) {
650  return false;
651  }
652  unset( $_lsExists );
653 
654  require "$IP/includes/DefaultSettings.php";
655  require "$IP/LocalSettings.php";
656 
657  return get_defined_vars();
658  }
659 
669  public function getFakePassword( $realPassword ) {
670  return str_repeat( '*', strlen( $realPassword ) );
671  }
672 
680  public function setPassword( $name, $value ) {
681  if ( !preg_match( '/^\*+$/', $value ) ) {
682  $this->setVar( $name, $value );
683  }
684  }
685 
697  public static function maybeGetWebserverPrimaryGroup() {
698  if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
699  # I don't know this, this isn't UNIX.
700  return null;
701  }
702 
703  # posix_getegid() *not* getmygid() because we want the group of the webserver,
704  # not whoever owns the current script.
705  $gid = posix_getegid();
706  return posix_getpwuid( $gid )['name'] ?? null;
707  }
708 
725  public function parse( $text, $lineStart = false ) {
726  $parser = MediaWikiServices::getInstance()->getParser();
727 
728  try {
729  $out = $parser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
730  $html = $out->getText( [
731  'enableSectionEditLinks' => false,
732  'unwrap' => true,
733  ] );
734  $html = Parser::stripOuterParagraph( $html );
735  } catch ( Wikimedia\Services\ServiceDisabledException $e ) {
736  $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
737  }
738 
739  return $html;
740  }
741 
745  public function getParserOptions() {
746  return $this->parserOptions;
747  }
748 
749  public function disableLinkPopups() {
750  $this->parserOptions->setExternalLinkTarget( false );
751  }
752 
753  public function restoreLinkPopups() {
754  global $wgExternalLinkTarget;
755  $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget );
756  }
757 
766  public function populateSiteStats( DatabaseInstaller $installer ) {
767  $status = $installer->getConnection();
768  if ( !$status->isOK() ) {
769  return $status;
770  }
771  // @phan-suppress-next-line PhanUndeclaredMethod
772  $status->value->insert(
773  'site_stats',
774  [
775  'ss_row_id' => 1,
776  'ss_total_edits' => 0,
777  'ss_good_articles' => 0,
778  'ss_total_pages' => 0,
779  'ss_users' => 0,
780  'ss_active_users' => 0,
781  'ss_images' => 0
782  ],
783  __METHOD__,
784  'IGNORE'
785  );
786 
787  return Status::newGood();
788  }
789 
794  protected function envCheckDB() {
795  global $wgLang;
797  $dbType = $this->getVar( 'wgDBtype' );
798 
799  $allNames = [];
800 
801  // Messages: config-type-mysql, config-type-postgres, config-type-sqlite
802  foreach ( self::getDBTypes() as $name ) {
803  $allNames[] = wfMessage( "config-type-$name" )->text();
804  }
805 
806  $databases = $this->getCompiledDBs();
807 
808  $databases = array_flip( $databases );
809  $ok = true;
810  foreach ( array_keys( $databases ) as $db ) {
811  $installer = $this->getDBInstaller( $db );
812  $status = $installer->checkPrerequisites();
813  if ( !$status->isGood() ) {
814  if ( !$this instanceof WebInstaller && $db === $dbType ) {
815  // Strictly check the key database type instead of just outputting message
816  // Note: No perform this check run from the web installer, since this method always called by
817  // the welcome page under web installation, so $dbType will always be 'mysql'
818  $ok = false;
819  }
820  $this->showStatusMessage( $status );
821  unset( $databases[$db] );
822  }
823  }
824  $databases = array_flip( $databases );
825  if ( !$databases ) {
826  $this->showError( 'config-no-db', $wgLang->commaList( $allNames ), count( $allNames ) );
827  return false;
828  }
829  return $ok;
830  }
831 
840  protected function envCheckPCRE() {
841  Wikimedia\suppressWarnings();
842  $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' );
843  // Need to check for \p support too, as PCRE can be compiled
844  // with utf8 support, but not unicode property support.
845  // check that \p{Zs} (space separators) matches
846  // U+3000 (Ideographic space)
847  $regexprop = preg_replace( '/\p{Zs}/u', '', "-\u{3000}-" );
848  Wikimedia\restoreWarnings();
849  if ( $regexd != '--' || $regexprop != '--' ) {
850  $this->showError( 'config-pcre-no-utf8' );
851 
852  return false;
853  }
854 
855  return true;
856  }
857 
862  protected function envCheckMemory() {
863  $limit = ini_get( 'memory_limit' );
864 
865  if ( !$limit || $limit == -1 ) {
866  return true;
867  }
868 
869  $n = wfShorthandToInteger( $limit );
870 
871  if ( $n < $this->minMemorySize * 1024 * 1024 ) {
872  $newLimit = "{$this->minMemorySize}M";
873 
874  if ( ini_set( "memory_limit", $newLimit ) === false ) {
875  $this->showMessage( 'config-memory-bad', $limit );
876  } else {
877  $this->showMessage( 'config-memory-raised', $limit, $newLimit );
878  $this->setVar( '_RaiseMemory', true );
879  }
880  }
881 
882  return true;
883  }
884 
888  protected function envCheckCache() {
889  $caches = [];
890  foreach ( $this->objectCaches as $name => $function ) {
891  if ( function_exists( $function ) ) {
892  $caches[$name] = true;
893  }
894  }
895 
896  if ( !$caches ) {
897  $this->showMessage( 'config-no-cache-apcu' );
898  }
899 
900  $this->setVar( '_Caches', $caches );
901  }
902 
907  protected function envCheckModSecurity() {
908  if ( self::apacheModulePresent( 'mod_security' )
909  || self::apacheModulePresent( 'mod_security2' ) ) {
910  $this->showMessage( 'config-mod-security' );
911  }
912 
913  return true;
914  }
915 
920  protected function envCheckDiff3() {
921  $names = [ "gdiff3", "diff3" ];
922  if ( wfIsWindows() ) {
923  $names[] = 'diff3.exe';
924  }
925  $versionInfo = [ '--version', 'GNU diffutils' ];
926 
927  $diff3 = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
928 
929  if ( $diff3 ) {
930  $this->setVar( 'wgDiff3', $diff3 );
931  } else {
932  $this->setVar( 'wgDiff3', false );
933  $this->showMessage( 'config-diff3-bad' );
934  }
935 
936  return true;
937  }
938 
943  protected function envCheckGraphics() {
944  $names = wfIsWindows() ? 'convert.exe' : 'convert';
945  $versionInfo = [ '-version', 'ImageMagick' ];
946  $convert = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
947 
948  $this->setVar( 'wgImageMagickConvertCommand', '' );
949  if ( $convert ) {
950  $this->setVar( 'wgImageMagickConvertCommand', $convert );
951  $this->showMessage( 'config-imagemagick', $convert );
952  } elseif ( function_exists( 'imagejpeg' ) ) {
953  $this->showMessage( 'config-gd' );
954  } else {
955  $this->showMessage( 'config-no-scaling' );
956  }
957 
958  return true;
959  }
960 
967  protected function envCheckGit() {
968  $names = wfIsWindows() ? 'git.exe' : 'git';
969  $versionInfo = [ '--version', 'git version' ];
970 
971  $git = ExecutableFinder::findInDefaultPaths( $names, $versionInfo );
972 
973  if ( $git ) {
974  $this->setVar( 'wgGitBin', $git );
975  $this->showMessage( 'config-git', $git );
976  } else {
977  $this->setVar( 'wgGitBin', false );
978  $this->showMessage( 'config-git-bad' );
979  }
980 
981  return true;
982  }
983 
989  protected function envCheckServer() {
990  $server = $this->envGetDefaultServer();
991  if ( $server !== null ) {
992  $this->showMessage( 'config-using-server', $server );
993  }
994  return true;
995  }
996 
1002  protected function envCheckPath() {
1003  $this->showMessage(
1004  'config-using-uri',
1005  $this->getVar( 'wgServer' ),
1006  $this->getVar( 'wgScriptPath' )
1007  );
1008  return true;
1009  }
1010 
1015  protected function envCheckUploadsDirectory() {
1016  global $IP;
1017 
1018  $dir = $IP . '/images/';
1019  $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/';
1020  $safe = !$this->dirIsExecutable( $dir, $url );
1021 
1022  if ( !$safe ) {
1023  $this->showMessage( 'config-uploads-not-safe', $dir );
1024  }
1025 
1026  return true;
1027  }
1028 
1034  protected function envCheckSuhosinMaxValueLength() {
1035  $currentValue = ini_get( 'suhosin.get.max_value_length' );
1036  $minRequired = 2000;
1037  $recommended = 5000;
1038  if ( $currentValue > 0 && $currentValue < $minRequired ) {
1039  $this->showError( 'config-suhosin-max-value-length', $currentValue, $minRequired, $recommended );
1040  return false;
1041  }
1042 
1043  return true;
1044  }
1045 
1052  protected function envCheck64Bit() {
1053  if ( PHP_INT_SIZE == 4 ) {
1054  $this->showMessage( 'config-using-32bit' );
1055  }
1056 
1057  return true;
1058  }
1059 
1063  protected function envCheckLibicu() {
1071  $not_normal_c = "\u{FA6C}";
1072  $normal_c = "\u{242EE}";
1073 
1074  $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C );
1075 
1076  $this->showMessage( 'config-unicode-using-intl' );
1077  if ( $intl !== $normal_c ) {
1078  $this->showMessage( 'config-unicode-update-warning' );
1079  }
1080  }
1081 
1085  protected function envPrepServer() {
1086  $server = $this->envGetDefaultServer();
1087  if ( $server !== null ) {
1088  $this->setVar( 'wgServer', $server );
1089  }
1090  }
1091 
1096  abstract protected function envGetDefaultServer();
1097 
1101  protected function envPrepPath() {
1102  global $IP;
1103  $IP = dirname( dirname( __DIR__ ) );
1104  $this->setVar( 'IP', $IP );
1105  }
1106 
1115  public function dirIsExecutable( $dir, $url ) {
1116  $scriptTypes = [
1117  'php' => [
1118  "<?php echo 'exec';",
1119  "#!/var/env php\n<?php echo 'exec';",
1120  ],
1121  ];
1122 
1123  // it would be good to check other popular languages here, but it'll be slow.
1124  // TODO no need to have a loop if there is going to only be one script type
1125 
1126  $httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
1127 
1128  Wikimedia\suppressWarnings();
1129 
1130  foreach ( $scriptTypes as $ext => $contents ) {
1131  foreach ( $contents as $source ) {
1132  $file = 'exectest.' . $ext;
1133 
1134  if ( !file_put_contents( $dir . $file, $source ) ) {
1135  break;
1136  }
1137 
1138  try {
1139  $text = $httpRequestFactory->get(
1140  $url . $file,
1141  [ 'timeout' => 3 ],
1142  __METHOD__
1143  );
1144  } catch ( Exception $e ) {
1145  // HttpRequestFactory::get can throw with allow_url_fopen = false and no curl
1146  // extension.
1147  $text = null;
1148  }
1149  unlink( $dir . $file );
1150 
1151  if ( $text == 'exec' ) {
1152  Wikimedia\restoreWarnings();
1153 
1154  return $ext;
1155  }
1156  }
1157  }
1158 
1159  Wikimedia\restoreWarnings();
1160 
1161  return false;
1162  }
1163 
1170  public static function apacheModulePresent( $moduleName ) {
1171  if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) {
1172  return true;
1173  }
1174  // try it the hard way
1175  ob_start();
1176  phpinfo( INFO_MODULES );
1177  $info = ob_get_clean();
1178 
1179  return strpos( $info, $moduleName ) !== false;
1180  }
1181 
1187  public function setParserLanguage( $lang ) {
1188  $this->parserOptions->setTargetLanguage( $lang );
1189  $this->parserOptions->setUserLang( $lang );
1190  }
1191 
1197  protected function getDocUrl( $page ) {
1198  return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
1199  }
1200 
1210  public function findExtensions( $directory = 'extensions' ) {
1211  switch ( $directory ) {
1212  case 'extensions':
1213  return $this->findExtensionsByType( 'extension', 'extensions' );
1214  case 'skins':
1215  return $this->findExtensionsByType( 'skin', 'skins' );
1216  default:
1217  throw new InvalidArgumentException( "Invalid extension type" );
1218  }
1219  }
1220 
1230  protected function findExtensionsByType( $type = 'extension', $directory = 'extensions' ) {
1231  if ( $this->getVar( 'IP' ) === null ) {
1232  return Status::newGood( [] );
1233  }
1234 
1235  $extDir = $this->getVar( 'IP' ) . '/' . $directory;
1236  if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) {
1237  return Status::newGood( [] );
1238  }
1239 
1240  // @phan-suppress-next-line SecurityCheck-PathTraversal False positive
1241  $dh = opendir( $extDir );
1242  $exts = [];
1243  $status = new Status;
1244  while ( ( $file = readdir( $dh ) ) !== false ) {
1245  // skip non-dirs and hidden directories
1246  if ( !is_dir( "$extDir/$file" ) || $file[0] === '.' ) {
1247  continue;
1248  }
1249  $extStatus = $this->getExtensionInfo( $type, $directory, $file );
1250  if ( $extStatus->isOK() ) {
1251  $exts[$file] = $extStatus->value;
1252  } elseif ( $extStatus->hasMessage( 'config-extension-not-found' ) ) {
1253  // (T225512) The directory is not actually an extension. Downgrade to warning.
1254  $status->warning( 'config-extension-not-found', $file );
1255  } else {
1256  $status->merge( $extStatus );
1257  }
1258  }
1259  closedir( $dh );
1260  uksort( $exts, 'strnatcasecmp' );
1261 
1262  $status->value = $exts;
1263 
1264  return $status;
1265  }
1266 
1274  protected function getExtensionInfo( $type, $parentRelPath, $name ) {
1275  if ( $this->getVar( 'IP' ) === null ) {
1276  throw new Exception( 'Cannot find extensions since the IP variable is not yet set' );
1277  }
1278  if ( $type !== 'extension' && $type !== 'skin' ) {
1279  throw new InvalidArgumentException( "Invalid extension type" );
1280  }
1281  $absDir = $this->getVar( 'IP' ) . "/$parentRelPath/$name";
1282  $relDir = "../$parentRelPath/$name";
1283  if ( !is_dir( $absDir ) ) {
1284  return Status::newFatal( 'config-extension-not-found', $name );
1285  }
1286  $jsonFile = $type . '.json';
1287  $fullJsonFile = "$absDir/$jsonFile";
1288  $isJson = file_exists( $fullJsonFile );
1289  $isPhp = false;
1290  if ( !$isJson ) {
1291  // Only fallback to PHP file if JSON doesn't exist
1292  $fullPhpFile = "$absDir/$name.php";
1293  $isPhp = file_exists( $fullPhpFile );
1294  }
1295  if ( !$isJson && !$isPhp ) {
1296  return Status::newFatal( 'config-extension-not-found', $name );
1297  }
1298 
1299  // Extension exists. Now see if there are screenshots
1300  $info = [];
1301  if ( is_dir( "$absDir/screenshots" ) ) {
1302  $paths = glob( "$absDir/screenshots/*.png" );
1303  foreach ( $paths as $path ) {
1304  $info['screenshots'][] = str_replace( $absDir, $relDir, $path );
1305  }
1306  }
1307 
1308  if ( $isJson ) {
1309  $jsonStatus = $this->readExtension( $fullJsonFile );
1310  if ( !$jsonStatus->isOK() ) {
1311  return $jsonStatus;
1312  }
1313  $info += $jsonStatus->value;
1314  }
1315 
1316  // @phan-suppress-next-line SecurityCheckMulti
1317  return Status::newGood( $info );
1318  }
1319 
1328  private function readExtension( $fullJsonFile, $extDeps = [], $skinDeps = [] ) {
1329  $load = [
1330  $fullJsonFile => 1
1331  ];
1332  if ( $extDeps ) {
1333  $extDir = $this->getVar( 'IP' ) . '/extensions';
1334  foreach ( $extDeps as $dep ) {
1335  $fname = "$extDir/$dep/extension.json";
1336  if ( !file_exists( $fname ) ) {
1337  return Status::newFatal( 'config-extension-not-found', $dep );
1338  }
1339  $load[$fname] = 1;
1340  }
1341  }
1342  if ( $skinDeps ) {
1343  $skinDir = $this->getVar( 'IP' ) . '/skins';
1344  foreach ( $skinDeps as $dep ) {
1345  $fname = "$skinDir/$dep/skin.json";
1346  if ( !file_exists( $fname ) ) {
1347  return Status::newFatal( 'config-extension-not-found', $dep );
1348  }
1349  $load[$fname] = 1;
1350  }
1351  }
1352  $registry = new ExtensionRegistry();
1353  try {
1354  $info = $registry->readFromQueue( $load );
1355  } catch ( ExtensionDependencyError $e ) {
1356  if ( $e->incompatibleCore || $e->incompatibleSkins
1357  || $e->incompatibleExtensions
1358  ) {
1359  // If something is incompatible with a dependency, we have no real
1360  // option besides skipping it
1361  return Status::newFatal( 'config-extension-dependency',
1362  basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1363  } elseif ( $e->missingExtensions || $e->missingSkins ) {
1364  // There's an extension missing in the dependency tree,
1365  // so add those to the dependency list and try again
1366  $status = $this->readExtension(
1367  $fullJsonFile,
1368  array_merge( $extDeps, $e->missingExtensions ),
1369  array_merge( $skinDeps, $e->missingSkins )
1370  );
1371  if ( !$status->isOK() && !$status->hasMessage( 'config-extension-dependency' ) ) {
1372  $status = Status::newFatal( 'config-extension-dependency',
1373  basename( dirname( $fullJsonFile ) ), $status->getMessage() );
1374  }
1375  return $status;
1376  }
1377  // Some other kind of dependency error?
1378  return Status::newFatal( 'config-extension-dependency',
1379  basename( dirname( $fullJsonFile ) ), $e->getMessage() );
1380  }
1381  $ret = [];
1382  // The order of credits will be the order of $load,
1383  // so the first extension is the one we want to load,
1384  // everything else is a dependency
1385  $i = 0;
1386  foreach ( $info['credits'] as $name => $credit ) {
1387  $i++;
1388  if ( $i == 1 ) {
1389  // Extension we want to load
1390  continue;
1391  }
1392  $type = basename( $credit['path'] ) === 'skin.json' ? 'skins' : 'extensions';
1393  $ret['requires'][$type][] = $credit['name'];
1394  }
1395  $credits = array_values( $info['credits'] )[0];
1396  if ( isset( $credits['url'] ) ) {
1397  $ret['url'] = $credits['url'];
1398  }
1399  $ret['type'] = $credits['type'];
1400 
1401  return Status::newGood( $ret );
1402  }
1403 
1412  public function getDefaultSkin( array $skinNames ) {
1413  $defaultSkin = $GLOBALS['wgDefaultSkin'];
1414  if ( !$skinNames || in_array( $defaultSkin, $skinNames ) ) {
1415  return $defaultSkin;
1416  } else {
1417  return $skinNames[0];
1418  }
1419  }
1420 
1426  protected function includeExtensions() {
1427  // Marker for DatabaseUpdater::loadExtensions so we don't
1428  // double load extensions
1429  define( 'MW_EXTENSIONS_LOADED', true );
1430 
1431  $legacySchemaHooks = $this->getAutoExtensionLegacyHooks();
1432  $data = $this->getAutoExtensionData();
1433  if ( isset( $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] ) ) {
1434  $legacySchemaHooks = array_merge( $legacySchemaHooks,
1435  $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] );
1436  }
1437  $extDeprecatedHooks = $data['attributes']['DeprecatedHooks'] ?? [];
1438 
1439  $this->autoExtensionHookContainer = new HookContainer(
1440  new StaticHookRegistry(
1441  [ 'LoadExtensionSchemaUpdates' => $legacySchemaHooks ],
1442  $data['attributes']['Hooks'] ?? [],
1443  $extDeprecatedHooks
1444  ),
1445  MediaWikiServices::getInstance()->getObjectFactory()
1446  );
1447 
1448  return Status::newGood();
1449  }
1450 
1458  protected function getAutoExtensionLegacyHooks() {
1459  $exts = $this->getVar( '_Extensions' );
1460  $installPath = $this->getVar( 'IP' );
1461  $files = [];
1462  foreach ( $exts as $e ) {
1463  if ( file_exists( "$installPath/extensions/$e/$e.php" ) ) {
1464  $files[] = "$installPath/extensions/$e/$e.php";
1465  }
1466  }
1467 
1468  if ( $files ) {
1469  return $this->includeExtensionFiles( $files );
1470  } else {
1471  return [];
1472  }
1473  }
1474 
1482  protected function includeExtensionFiles( $files ) {
1483  global $IP;
1484  $IP = $this->getVar( 'IP' );
1485 
1494  // @phan-suppress-next-line SecurityCheck-PathTraversal
1495  require "$IP/includes/DefaultSettings.php";
1496 
1497  // phpcs:ignore MediaWiki.VariableAnalysis.UnusedGlobalVariables
1498  global $wgAutoloadClasses;
1499  foreach ( $files as $file ) {
1500  require_once $file;
1501  }
1502 
1503  // @phpcs:disable MediaWiki.VariableAnalysis.MisleadingGlobalNames.Misleading$wgHooks
1504  // @phan-suppress-next-line PhanUndeclaredVariable,PhanCoalescingAlwaysNull $wgHooks is set by DefaultSettings
1505  $hooksWeWant = $wgHooks['LoadExtensionSchemaUpdates'] ?? [];
1506  // @phpcs:enable MediaWiki.VariableAnalysis.MisleadingGlobalNames.Misleading$wgHooks
1507 
1508  // Ignore everyone else's hooks. Lord knows what someone might be doing
1509  // in ParserFirstCallInit (see T29171)
1510  return [ 'LoadExtensionSchemaUpdates' => $hooksWeWant ];
1511  }
1512 
1519  protected function getAutoExtensionData() {
1520  $exts = $this->getVar( '_Extensions' );
1521  $installPath = $this->getVar( 'IP' );
1522  $queue = [];
1523  foreach ( $exts as $e ) {
1524  if ( file_exists( "$installPath/extensions/$e/extension.json" ) ) {
1525  $queue["$installPath/extensions/$e/extension.json"] = 1;
1526  }
1527  }
1528 
1529  $registry = new ExtensionRegistry();
1530  $data = $registry->readFromQueue( $queue );
1531  global $wgAutoloadClasses;
1532  $wgAutoloadClasses += $data['globals']['wgAutoloadClasses'];
1533  return $data;
1534  }
1535 
1543  public function getAutoExtensionHookContainer() {
1544  if ( !$this->autoExtensionHookContainer ) {
1545  throw new \Exception( __METHOD__ .
1546  ': includeExtensions() has not been called' );
1547  }
1549  }
1550 
1564  protected function getInstallSteps( DatabaseInstaller $installer ) {
1565  $coreInstallSteps = [
1566  [ 'name' => 'database', 'callback' => [ $installer, 'setupDatabase' ] ],
1567  [ 'name' => 'tables', 'callback' => [ $installer, 'createTables' ] ],
1568  [ 'name' => 'tables-manual', 'callback' => [ $installer, 'createManualTables' ] ],
1569  [ 'name' => 'interwiki', 'callback' => [ $installer, 'populateInterwikiTable' ] ],
1570  [ 'name' => 'stats', 'callback' => [ $this, 'populateSiteStats' ] ],
1571  [ 'name' => 'keys', 'callback' => [ $this, 'generateKeys' ] ],
1572  [ 'name' => 'updates', 'callback' => [ $installer, 'insertUpdateKeys' ] ],
1573  [ 'name' => 'restore-services', 'callback' => [ $this, 'restoreServices' ] ],
1574  [ 'name' => 'sysop', 'callback' => [ $this, 'createSysop' ] ],
1575  [ 'name' => 'mainpage', 'callback' => [ $this, 'createMainpage' ] ],
1576  ];
1577 
1578  // Build the array of install steps starting from the core install list,
1579  // then adding any callbacks that wanted to attach after a given step
1580  foreach ( $coreInstallSteps as $step ) {
1581  $this->installSteps[] = $step;
1582  if ( isset( $this->extraInstallSteps[$step['name']] ) ) {
1583  $this->installSteps = array_merge(
1584  $this->installSteps,
1585  $this->extraInstallSteps[$step['name']]
1586  );
1587  }
1588  }
1589 
1590  // Prepend any steps that want to be at the beginning
1591  if ( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
1592  $this->installSteps = array_merge(
1593  $this->extraInstallSteps['BEGINNING'],
1594  $this->installSteps
1595  );
1596  }
1597 
1598  // Extensions should always go first, chance to tie into hooks and such
1599  if ( count( $this->getVar( '_Extensions' ) ) ) {
1600  array_unshift( $this->installSteps,
1601  [ 'name' => 'extensions', 'callback' => [ $this, 'includeExtensions' ] ]
1602  );
1603  $this->installSteps[] = [
1604  'name' => 'extension-tables',
1605  'callback' => [ $installer, 'createExtensionTables' ]
1606  ];
1607  }
1608 
1609  return $this->installSteps;
1610  }
1611 
1620  public function performInstallation( $startCB, $endCB ) {
1621  $installResults = [];
1622  $installer = $this->getDBInstaller();
1623  $installer->preInstall();
1624  $steps = $this->getInstallSteps( $installer );
1625  foreach ( $steps as $stepObj ) {
1626  $name = $stepObj['name'];
1627  call_user_func_array( $startCB, [ $name ] );
1628 
1629  // Perform the callback step
1630  $status = call_user_func( $stepObj['callback'], $installer );
1631 
1632  // Output and save the results
1633  call_user_func( $endCB, $name, $status );
1634  $installResults[$name] = $status;
1635 
1636  // If we've hit some sort of fatal, we need to bail.
1637  // Callback already had a chance to do output above.
1638  if ( !$status->isOK() ) {
1639  break;
1640  }
1641  }
1642  if ( $status->isOK() ) {
1643  $this->showMessage(
1644  'config-install-db-success'
1645  );
1646  $this->setVar( '_InstallDone', true );
1647  }
1648 
1649  return $installResults;
1650  }
1651 
1657  public function generateKeys() {
1658  $keys = [ 'wgSecretKey' => 64 ];
1659  if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
1660  $keys['wgUpgradeKey'] = 16;
1661  }
1662 
1663  return $this->doGenerateKeys( $keys );
1664  }
1665 
1670  public function restoreServices() {
1671  $this->resetMediaWikiServices( null, [
1672  'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
1673  return $services->get( 'UserOptionsManager' );
1674  }
1675  ] );
1676  return Status::newGood();
1677  }
1678 
1685  protected function doGenerateKeys( $keys ) {
1686  foreach ( $keys as $name => $length ) {
1687  $secretKey = MWCryptRand::generateHex( $length );
1688  $this->setVar( $name, $secretKey );
1689  }
1690  return Status::newGood();
1691  }
1692 
1698  protected function createSysop() {
1699  $name = $this->getVar( '_AdminName' );
1700  $user = User::newFromName( $name );
1701 
1702  if ( !$user ) {
1703  // We should've validated this earlier anyway!
1704  return Status::newFatal( 'config-admin-error-user', $name );
1705  }
1706 
1707  if ( $user->idForName() == 0 ) {
1708  $user->addToDatabase();
1709 
1710  $password = $this->getVar( '_AdminPassword' );
1711  $status = $user->changeAuthenticationData( [
1712  'username' => $user->getName(),
1713  'password' => $password,
1714  'retype' => $password,
1715  ] );
1716  if ( !$status->isGood() ) {
1717  return Status::newFatal( 'config-admin-error-password',
1718  $name, $status->getWikiText( false, false, $this->getVar( '_UserLang' ) ) );
1719  }
1720 
1721  $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager();
1722  $userGroupManager->addUserToGroup( $user, 'sysop' );
1723  $userGroupManager->addUserToGroup( $user, 'bureaucrat' );
1724  $userGroupManager->addUserToGroup( $user, 'interface-admin' );
1725  if ( $this->getVar( '_AdminEmail' ) ) {
1726  $user->setEmail( $this->getVar( '_AdminEmail' ) );
1727  }
1728  $user->saveSettings();
1729 
1730  // Update user count
1731  $ssUpdate = SiteStatsUpdate::factory( [ 'users' => 1 ] );
1732  $ssUpdate->doUpdate();
1733  }
1734 
1735  if ( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
1736  return $this->subscribeToMediaWikiAnnounce();
1737  }
1738  return Status::newGood();
1739  }
1740 
1744  private function subscribeToMediaWikiAnnounce() {
1745  $status = Status::newGood();
1746  $http = MediaWikiServices::getInstance()->getHttpRequestFactory();
1747  if ( !$http->canMakeRequests() ) {
1748  $status->warning( 'config-install-subscribe-fail',
1749  wfMessage( 'config-install-subscribe-notpossible' ) );
1750  return $status;
1751  }
1752 
1753  // Create subscription request
1754  $params = [ 'email' => $this->getVar( '_AdminEmail' ) ];
1755  $req = $http->create( self::MEDIAWIKI_ANNOUNCE_URL . 'anonymous_subscribe',
1756  [ 'method' => 'POST', 'postData' => $params ], __METHOD__ );
1757 
1758  // Add headers needed to pass Django's CSRF checks
1759  $token = str_repeat( 'a', 64 );
1760  $req->setHeader( 'Referer', self::MEDIAWIKI_ANNOUNCE_URL );
1761  $req->setHeader( 'Cookie', "csrftoken=$token" );
1762  $req->setHeader( 'X-CSRFToken', $token );
1763 
1764  // Send subscription request
1765  $reqStatus = $req->execute();
1766  if ( !$reqStatus->isOK() ) {
1767  $status->warning( 'config-install-subscribe-fail',
1768  Status::wrap( $reqStatus )->getMessage() );
1769  return $status;
1770  }
1771 
1772  // Was the request submitted successfully?
1773  // The status message is displayed after a redirect, using Django's messages
1774  // framework, so load the list summary page and look for the expected text.
1775  // (Though parsing the cookie set by the framework may be possible, it isn't
1776  // simple, since the format of the cookie has changed between versions.)
1777  $checkReq = $http->create( self::MEDIAWIKI_ANNOUNCE_URL, [], __METHOD__ );
1778  $checkReq->setCookieJar( $req->getCookieJar() );
1779  if ( !$checkReq->execute()->isOK() ) {
1780  $status->warning( 'config-install-subscribe-possiblefail' );
1781  return $status;
1782  }
1783  $html = $checkReq->getContent();
1784  if ( strpos( $html, 'Please check your inbox for further instructions' ) !== false ) {
1785  // Success
1786  } elseif ( strpos( $html, 'Member already subscribed' ) !== false ) {
1787  $status->warning( 'config-install-subscribe-alreadysubscribed' );
1788  } elseif ( strpos( $html, 'Subscription request already pending' ) !== false ) {
1789  $status->warning( 'config-install-subscribe-alreadypending' );
1790  } else {
1791  $status->warning( 'config-install-subscribe-possiblefail' );
1792  }
1793  return $status;
1794  }
1795 
1802  protected function createMainpage( DatabaseInstaller $installer ) {
1803  $status = Status::newGood();
1805  if ( $title->exists() ) {
1806  $status->warning( 'config-install-mainpage-exists' );
1807  return $status;
1808  }
1809  try {
1810  $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title );
1811  $content = new WikitextContent(
1812  wfMessage( 'mainpagetext' )->inContentLanguage()->text() . "\n\n" .
1813  wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text()
1814  );
1815 
1816  $status = $page->doUserEditContent(
1817  $content,
1818  User::newSystemUser( 'MediaWiki default' ),
1819  '',
1820  EDIT_NEW
1821  );
1822  } catch ( Exception $e ) {
1823  // using raw, because $wgShowExceptionDetails can not be set yet
1824  $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
1825  }
1826 
1827  return $status;
1828  }
1829 
1833  public static function overrideConfig() {
1834  // Use PHP's built-in session handling, since MediaWiki's
1835  // SessionHandler can't work before we have an object cache set up.
1836  if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) {
1837  define( 'MW_NO_SESSION_HANDLER', 1 );
1838  }
1839 
1840  // Don't access the database
1841  $GLOBALS['wgUseDatabaseMessages'] = false;
1842  // Don't cache langconv tables
1843  $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE;
1844  // Don't try to cache ResourceLoader dependencies in the database
1845  $GLOBALS['wgResourceLoaderUseObjectCacheForDeps'] = true;
1846  // Debug-friendly
1847  $GLOBALS['wgShowExceptionDetails'] = true;
1848  $GLOBALS['wgShowHostnames'] = true;
1849  // Don't break forms
1850  $GLOBALS['wgExternalLinkTarget'] = '_blank';
1851 
1852  // Allow multiple ob_flush() calls
1853  $GLOBALS['wgDisableOutputCompression'] = true;
1854 
1855  // Use a sensible cookie prefix (not my_wiki)
1856  $GLOBALS['wgCookiePrefix'] = 'mw_installer';
1857 
1858  // Some of the environment checks make shell requests, remove limits
1859  $GLOBALS['wgMaxShellMemory'] = 0;
1860 
1861  // Override the default CookieSessionProvider with a dummy
1862  // implementation that won't stomp on PHP's cookies.
1863  $GLOBALS['wgSessionProviders'] = [
1864  [
1865  'class' => InstallerSessionProvider::class,
1866  'args' => [ [
1867  'priority' => 1,
1868  ] ]
1869  ]
1870  ];
1871 
1872  // Don't use the DB as the main stash
1873  $GLOBALS['wgMainStash'] = CACHE_NONE;
1874 
1875  // Don't try to use any object cache for SessionManager either.
1876  $GLOBALS['wgSessionCacheType'] = CACHE_NONE;
1877 
1878  // Set a dummy $wgServer to bypass the check in Setup.php, the
1879  // web installer will automatically detect it and not use this value.
1880  $GLOBALS['wgServer'] = 'https://🌻.invalid';
1881  }
1882 
1890  public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
1891  $this->extraInstallSteps[$findStep][] = $callback;
1892  }
1893 
1898  protected function disableTimeLimit() {
1899  Wikimedia\suppressWarnings();
1900  set_time_limit( 0 );
1901  Wikimedia\restoreWarnings();
1902  }
1903 }
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:45
Installer\__construct
__construct()
Constructor, always call this from child classes.
Definition: Installer.php:402
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:636
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:377
Installer\getAutoExtensionData
getAutoExtensionData()
Auto-detect extensions with an extension.json file.
Definition: Installer.php:1519
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:1802
Installer\subscribeToMediaWikiAnnounce
subscribeToMediaWikiAnnounce()
Definition: Installer.php:1744
Installer\parse
parse( $text, $lineStart=false)
Convert wikitext $text to HTML.
Definition: Installer.php:725
ExtensionDependencyError
Copyright (C) 2018 Kunal Mehta legoktm@debian.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:203
$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:1115
Installer\envCheckLibicu
envCheckLibicu()
Check the libicu version.
Definition: Installer.php:1063
ExtensionRegistry
The Registry loads JSON files, and uses a Processor to extract information from them.
Definition: ExtensionRegistry.php:15
Installer\populateSiteStats
populateSiteStats(DatabaseInstaller $installer)
Install step which adds a row to the site_stats table with appropriate initial values.
Definition: Installer.php:766
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:33
Installer\$extraInstallSteps
array $extraInstallSteps
Extra steps for installation, for things like DatabaseInstallers to modify.
Definition: Installer.php:250
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:267
Installer\envCheckUploadsDirectory
envCheckUploadsDirectory()
Environment check for the permissions of the uploads directory.
Definition: Installer.php:1015
Installer\$autoExtensionHookContainer
HookContainer null $autoExtensionHookContainer
Definition: Installer.php:329
Installer\$settings
array $settings
Definition: Installer.php:72
Installer\envPrepServer
envPrepServer()
Environment prep for the server hostname.
Definition: Installer.php:1085
$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:1620
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:595
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1167
MediaWiki\Interwiki\NullInterwikiLookup
An interwiki lookup that has no data, intended for use in the installer.
Definition: NullInterwikiLookup.php:29
StubGlobalUser\setUser
static setUser( $user)
Reset the stub global user to a different "real" user object, while ensuring that any method calls on...
Definition: StubGlobalUser.php:79
Installer\$dbInstallers
array $dbInstallers
Cached DB installer instances, access using getDBInstaller().
Definition: Installer.php:86
Installer\setParserLanguage
setParserLanguage( $lang)
ParserOptions are constructed before we determined the language, so fix it.
Definition: Installer.php:1187
Title\newMainPage
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:710
Installer\addInstallStep
addInstallStep( $callback, $findStep='BEGINNING')
Add an installation step following the given step.
Definition: Installer.php:1890
Installer\$internalDefaults
array $internalDefaults
Variables that are stored alongside globals, and are used for any configuration of the installation p...
Definition: Installer.php:206
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:680
Installer\envGetDefaultServer
envGetDefaultServer()
Helper function to be called from envPrepServer()
$wgLang
$wgLang
Definition: Setup.php:861
Installer\setVar
setVar( $name, $value)
Set a MW configuration variable, or internal installer configuration variable.
Definition: Installer.php:564
Installer\restoreServices
restoreServices()
Restore services that have been redefined in the early stage of installation.
Definition: Installer.php:1670
Installer\$parserTitle
Title $parserTitle
Cached Title, used by parse().
Definition: Installer.php:100
Installer\$objectCaches
array $objectCaches
Known object cache types and the functions used to test for their existence.
Definition: Installer.php:257
Installer\$minMemorySize
int $minMemorySize
Minimum memory size in MiB.
Definition: Installer.php:93
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:803
Installer\overrideConfig
static overrideConfig()
Override the necessary bits of the config to run an installation.
Definition: Installer.php:1833
Installer\createSysop
createSysop()
Create the first user account, grant it sysop, bureaucrat and interface-admin rights.
Definition: Installer.php:1698
Installer\getFakePassword
getFakePassword( $realPassword)
Get a fake password for sending back to the user in HTML.
Definition: Installer.php:669
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:1034
$wgHooks
$wgHooks
Global list of hooks.
Definition: DefaultSettings.php:8681
Installer\envPrepPath
envPrepPath()
Environment prep for setting $IP and $wgScriptPath.
Definition: Installer.php:1101
Installer\includeExtensionFiles
includeExtensionFiles( $files)
Include the specified extension PHP files.
Definition: Installer.php:1482
Installer\envCheckDiff3
envCheckDiff3()
Search for GNU diff3.
Definition: Installer.php:920
Config\get
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Status\wrap
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Definition: Status.php:62
Installer\generateKeys
generateKeys()
Generate $wgSecretKey.
Definition: Installer.php:1657
CACHE_MEMCACHED
const CACHE_MEMCACHED
Definition: Defines.php:88
Installer\envCheckMemory
envCheckMemory()
Environment check for available memory.
Definition: Installer.php:862
$wgObjectCaches
$wgObjectCaches
Advanced object cache configuration.
Definition: DefaultSettings.php:2826
Installer\getCompiledDBs
getCompiledDBs()
Get a list of DBs supported by current PHP setup.
Definition: Installer.php:587
$queue
$queue
Definition: mergeMessageFileList.php:176
Installer\envCheckCache
envCheckCache()
Environment check for compiled object cache types.
Definition: Installer.php:888
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:125
Installer\getExistingLocalSettings
static getExistingLocalSettings()
Determine if LocalSettings.php exists.
Definition: Installer.php:629
Installer\envCheckPath
envCheckPath()
Environment check to inform user which paths we've assumed.
Definition: Installer.php:1002
Installer\doGenerateKeys
doGenerateKeys( $keys)
Generate a secret value for variables using a secure generator.
Definition: Installer.php:1685
Installer\$parserOptions
ParserOptions $parserOptions
Cached ParserOptions, used by parse().
Definition: Installer.php:107
$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:5010
Installer\$dbTypes
static array $dbTypes
Known database types.
Definition: Installer.php:118
Installer\getVar
getVar( $name, $default=null)
Get an MW configuration variable, or internal installer configuration variable.
Definition: Installer.php:578
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:36
Installer\restoreLinkPopups
restoreLinkPopups()
Definition: Installer.php:753
Installer\getDefaultSkin
getDefaultSkin(array $skinNames)
Returns a default value to be used for $wgDefaultSkin: normally the one set in DefaultSettings,...
Definition: Installer.php:1412
Installer\findExtensions
findExtensions( $directory='extensions')
Find extensions or skins in a subdirectory of $IP.
Definition: Installer.php:1210
Installer\getInstallerConfig
static getInstallerConfig(Config $baseConfig)
Constructs a Config object that contains configuration settings that should be overwritten for the in...
Definition: Installer.php:364
$content
$content
Definition: router.php:76
Installer\$envPreps
array $envPreps
A list of environment preparation methods called by doEnvironmentPreps().
Definition: Installer.php:157
$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:1687
Installer\doEnvironmentPreps
doEnvironmentPreps()
Definition: Installer.php:552
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:446
Installer\readExtension
readExtension( $fullJsonFile, $extDeps=[], $skinDeps=[])
Definition: Installer.php:1328
Installer\getDBInstaller
getDBInstaller( $type=false)
Get an instance of DatabaseInstaller for the specified DB type.
Definition: Installer.php:609
DatabaseInstaller
Base class for DBMS-specific installation helper classes.
Definition: DatabaseInstaller.php:37
Installer\envCheckDB
envCheckDB()
Environment check for DB types.
Definition: Installer.php:794
Installer\getInstallSteps
getInstallSteps(DatabaseInstaller $installer)
Get an array of install steps.
Definition: Installer.php:1564
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:484
$wgAutoloadClasses
$wgAutoloadClasses
Array mapping class names to filenames, for autoloading.
Definition: DefaultSettings.php:8575
Installer\$compiledDBs
array $compiledDBs
List of detected DBs, access using getCompiledDBs().
Definition: Installer.php:79
Installer\getAutoExtensionHookContainer
getAutoExtensionHookContainer()
Get the hook container previously populated by includeExtensions().
Definition: Installer.php:1543
Installer\MEDIAWIKI_ANNOUNCE_URL
const MEDIAWIKI_ANNOUNCE_URL
URL to mediawiki-announce list summary page.
Definition: Installer.php:66
Installer\doEnvironmentChecks
doEnvironmentChecks()
Do initial checks of the PHP environment.
Definition: Installer.php:527
Installer\envCheckGraphics
envCheckGraphics()
Environment check for ImageMagick and GD.
Definition: Installer.php:943
wfShorthandToInteger
wfShorthandToInteger( $string='', $default=-1)
Converts shorthand byte notation to integer form.
Definition: GlobalFunctions.php:2406
Installer\apacheModulePresent
static apacheModulePresent( $moduleName)
Checks for presence of an Apache module.
Definition: Installer.php:1170
Title
Represents a title within MediaWiki.
Definition: Title.php:47
Installer\envCheckPCRE
envCheckPCRE()
Environment check for the PCRE module.
Definition: Installer.php:840
Parser\stripOuterParagraph
static stripOuterParagraph( $html)
Strip outer.
Definition: Parser.php:6346
Installer\getParserOptions
getParserOptions()
Definition: Installer.php:745
$path
$path
Definition: NoLocalSettings.php:25
Installer\envCheck64Bit
envCheck64Bit()
Checks if we're running on 64 bit or not.
Definition: Installer.php:1052
Installer\$licenses
array $licenses
License types.
Definition: Installer.php:292
Installer\disableTimeLimit
disableTimeLimit()
Disable the time limit for execution.
Definition: Installer.php:1898
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:989
Installer
Base installer class.
Definition: Installer.php:53
$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:61
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:1230
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:510
Installer\disableLinkPopups
disableLinkPopups()
Definition: Installer.php:749
Installer\getAutoExtensionLegacyHooks
getAutoExtensionLegacyHooks()
Auto-detect extensions with an old style .php registration file, load the extensions,...
Definition: Installer.php:1458
Installer\$installSteps
array[] $installSteps
The actual list of installation steps.
Definition: Installer.php:243
$IP
$IP
Definition: WebStart.php:49
Installer\includeExtensions
includeExtensions()
Installs the auto-detected extensions.
Definition: Installer.php:1426
$wgStyleDirectory
$wgStyleDirectory
Filesystem stylesheets directory.
Definition: DefaultSettings.php:255
Installer\envCheckGit
envCheckGit()
Search for git.
Definition: Installer.php:967
Installer\getDocUrl
getDocUrl( $page)
Overridden by WebInstaller to provide lastPage parameters.
Definition: Installer.php:1197
Installer\maybeGetWebserverPrimaryGroup
static maybeGetWebserverPrimaryGroup()
On POSIX systems return the primary group of the webserver we're running under.
Definition: Installer.php:697
Installer\getExtensionInfo
getExtensionInfo( $type, $parentRelPath, $name)
Definition: Installer.php:1274
Installer\$defaultVarNames
array $defaultVarNames
MediaWiki configuration globals that will eventually be passed through to LocalSettings....
Definition: Installer.php:169
Installer\getDBInstallerClass
static getDBInstallerClass( $type)
Get the DatabaseInstaller class name for this type.
Definition: Installer.php:598
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:135
$type
$type
Definition: testCompression.php:52
Installer\envCheckModSecurity
envCheckModSecurity()
Scare user to death if they have mod_security or mod_security2.
Definition: Installer.php:907