1<?php 2/** 3 * The setup for all MediaWiki processes (both web-based and CLI). 4 * 5 * This file must be included by all entry points (such as WebStart.php and doMaintenance.php). 6 * - The entry point MUST do these: 7 * - define the 'MEDIAWIKI' constant. 8 * - define the $IP global variable. 9 * - The entry point SHOULD do these: 10 * - define the 'MW_ENTRY_POINT' constant. 11 * - display an error if MW_CONFIG_CALLBACK is not defined and the 12 * the file specified in MW_CONFIG_FILE (or the $IP/LocalSettings.php default) 13 * does not exist. The error should either be sent before and instead 14 * of the Setup.php inclusion, or (if it needs classes and dependencies 15 * from core) the erorr can be displayed via a MW_CONFIG_CALLBACK, 16 * which must then abort the process to prevent the rest of Setup.php 17 * from executing. 18 * 19 * It does: 20 * - run-time environment checks, 21 * - load autoloaders, constants, default settings, and global functions, 22 * - load the site configuration (e.g. LocalSettings.php), 23 * - load the enabled extensions (via ExtensionRegistry), 24 * - expand any dynamic site configuration defaults and shortcuts 25 * - initialization of: 26 * - PHP run-time (setlocale, memory limit, default date timezone) 27 * - the debug logger (MWDebug) 28 * - the service container (MediaWikiServices) 29 * - the exception handler (MWExceptionHandler) 30 * - the session manager (SessionManager) 31 * 32 * This program is free software; you can redistribute it and/or modify 33 * it under the terms of the GNU General Public License as published by 34 * the Free Software Foundation; either version 2 of the License, or 35 * (at your option) any later version. 36 * 37 * This program is distributed in the hope that it will be useful, 38 * but WITHOUT ANY WARRANTY; without even the implied warranty of 39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 * GNU General Public License for more details. 41 * 42 * You should have received a copy of the GNU General Public License along 43 * with this program; if not, write to the Free Software Foundation, Inc., 44 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 45 * http://www.gnu.org/copyleft/gpl.html 46 * 47 * @file 48 */ 49 50use MediaWiki\Logger\LoggerFactory; 51use MediaWiki\MediaWikiServices; 52use Psr\Log\LoggerInterface; 53use Wikimedia\Rdbms\ChronologyProtector; 54use Wikimedia\Rdbms\LBFactory; 55 56/** 57 * Environment checks 58 * 59 * These are inline checks done before we include any source files, 60 * and thus these conditions may be assumed by all source code. 61 */ 62 63// This file must be included from a valid entry point (e.g. WebStart.php, Maintenance.php) 64if ( !defined( 'MEDIAWIKI' ) ) { 65 exit( 1 ); 66} 67 68// This file must have global scope. 69$wgScopeTest = 'MediaWiki Setup.php scope test'; 70if ( !isset( $GLOBALS['wgScopeTest'] ) || $GLOBALS['wgScopeTest'] !== $wgScopeTest ) { 71 echo "Error, Setup.php must be included from the file scope.\n"; 72 die( 1 ); 73} 74unset( $wgScopeTest ); 75 76// PHP must not be configured to overload mbstring functions. (T5782, T122807) 77// This was deprecated by upstream in PHP 7.2, likely to be removed in PHP 8.0. 78if ( ini_get( 'mbstring.func_overload' ) ) { 79 die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' ); 80} 81 82// The MW_ENTRY_POINT constant must always exists, to make it safe to access. 83// For compat, we do support older and custom MW entryoints that don't set this, 84// in which case we assign a default here. 85if ( !defined( 'MW_ENTRY_POINT' ) ) { 86 /** 87 * The entry point, which may be either the script filename without the 88 * file extension, or "cli" for maintenance scripts, or "unknown" for any 89 * entry point that does not set the constant. 90 */ 91 define( 'MW_ENTRY_POINT', 'unknown' ); 92} 93 94/** 95 * Pre-config setup: Before loading LocalSettings.php 96 * 97 * These are changes and additions to runtime that don't vary on site configuration. 98 */ 99 100require_once "$IP/includes/AutoLoader.php"; 101require_once "$IP/includes/Defines.php"; 102require_once "$IP/includes/DefaultSettings.php"; 103require_once "$IP/includes/GlobalFunctions.php"; 104 105// Load composer's autoloader if present 106if ( is_readable( "$IP/vendor/autoload.php" ) ) { 107 require_once "$IP/vendor/autoload.php"; 108} elseif ( file_exists( "$IP/vendor/autoload.php" ) ) { 109 die( "$IP/vendor/autoload.php exists but is not readable" ); 110} 111 112// Assert that composer dependencies were successfully loaded 113if ( !interface_exists( LoggerInterface::class ) ) { 114 $message = ( 115 'MediaWiki requires the <a href="https://github.com/php-fig/log">PSR-3 logging ' . 116 "library</a> to be present. This library is not embedded directly in MediaWiki's " . 117 "git repository and must be installed separately by the end user.\n\n" . 118 'Please see the <a href="https://www.mediawiki.org/wiki/Download_from_Git' . 119 '#Fetch_external_libraries">instructions for installing libraries</a> on mediawiki.org ' . 120 'for help on installing the required components.' 121 ); 122 echo $message; 123 trigger_error( $message, E_USER_ERROR ); 124 die( 1 ); 125} 126 127MediaWiki\HeaderCallback::register(); 128 129// Set the encoding used by PHP for reading HTTP input, and writing output. 130// This is also the default for mbstring functions. 131mb_internal_encoding( 'UTF-8' ); 132 133/** 134 * Load LocalSettings.php 135 */ 136 137if ( defined( 'MW_CONFIG_CALLBACK' ) ) { 138 call_user_func( MW_CONFIG_CALLBACK ); 139} else { 140 if ( !defined( 'MW_CONFIG_FILE' ) ) { 141 define( 'MW_CONFIG_FILE', "$IP/LocalSettings.php" ); 142 } 143 require_once MW_CONFIG_FILE; 144} 145 146/** 147 * Customization point after all loading (constants, functions, classes, 148 * DefaultSettings, LocalSettings). Specifically, this is before usage of 149 * settings, before instantiation of Profiler (and other singletons), and 150 * before any setup functions or hooks run. 151 */ 152 153if ( defined( 'MW_SETUP_CALLBACK' ) ) { 154 call_user_func( MW_SETUP_CALLBACK ); 155} 156 157/** 158 * Load queued extensions 159 */ 160 161ExtensionRegistry::getInstance()->loadFromQueue(); 162// Don't let any other extensions load 163ExtensionRegistry::getInstance()->finish(); 164 165// Set the configured locale on all requests for consistency 166// This must be after LocalSettings.php (and is informed by the installer). 167putenv( "LC_ALL=$wgShellLocale" ); 168setlocale( LC_ALL, $wgShellLocale ); 169 170/** 171 * Expand dynamic defaults and shortcuts 172 */ 173 174if ( $wgScript === false ) { 175 $wgScript = "$wgScriptPath/index.php"; 176} 177if ( $wgLoadScript === false ) { 178 $wgLoadScript = "$wgScriptPath/load.php"; 179} 180if ( $wgRestPath === false ) { 181 $wgRestPath = "$wgScriptPath/rest.php"; 182} 183if ( $wgArticlePath === false ) { 184 if ( $wgUsePathInfo ) { 185 $wgArticlePath = "$wgScript/$1"; 186 } else { 187 $wgArticlePath = "$wgScript?title=$1"; 188 } 189} 190if ( $wgResourceBasePath === null ) { 191 $wgResourceBasePath = $wgScriptPath; 192} 193if ( $wgStylePath === false ) { 194 $wgStylePath = "$wgResourceBasePath/skins"; 195} 196if ( $wgLocalStylePath === false ) { 197 // Avoid wgResourceBasePath here since that may point to a different domain (e.g. CDN) 198 $wgLocalStylePath = "$wgScriptPath/skins"; 199} 200if ( $wgExtensionAssetsPath === false ) { 201 $wgExtensionAssetsPath = "$wgResourceBasePath/extensions"; 202} 203 204// For backwards compatibility, the value of wgLogos is copied to wgLogo. 205// This is because some extensions/skins may be using $config->get('Logo') 206// to access the value. 207if ( $wgLogos !== false && isset( $wgLogos['1x'] ) ) { 208 $wgLogo = $wgLogos['1x']; 209} 210if ( $wgLogo === false ) { 211 $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png"; 212} 213 214if ( $wgUploadPath === false ) { 215 $wgUploadPath = "$wgScriptPath/images"; 216} 217if ( $wgUploadDirectory === false ) { 218 $wgUploadDirectory = "$IP/images"; 219} 220if ( $wgReadOnlyFile === false ) { 221 $wgReadOnlyFile = "{$wgUploadDirectory}/lock_yBgMBwiR"; 222} 223if ( $wgFileCacheDirectory === false ) { 224 $wgFileCacheDirectory = "{$wgUploadDirectory}/cache"; 225} 226if ( $wgDeletedDirectory === false ) { 227 $wgDeletedDirectory = "{$wgUploadDirectory}/deleted"; 228} 229if ( $wgGitInfoCacheDirectory === false && $wgCacheDirectory !== false ) { 230 $wgGitInfoCacheDirectory = "{$wgCacheDirectory}/gitinfo"; 231} 232if ( $wgSharedPrefix === false ) { 233 $wgSharedPrefix = $wgDBprefix; 234} 235if ( $wgSharedSchema === false ) { 236 $wgSharedSchema = $wgDBmwschema; 237} 238if ( $wgMetaNamespace === false ) { 239 $wgMetaNamespace = str_replace( ' ', '_', $wgSitename ); 240} 241 242// Blacklisted file extensions shouldn't appear on the "allowed" list 243$wgFileExtensions = array_values( array_diff( $wgFileExtensions, $wgFileBlacklist ) ); 244 245// Fix path to icon images after they were moved in 1.24 246if ( $wgRightsIcon ) { 247 $wgRightsIcon = str_replace( 248 "{$wgStylePath}/common/images/", 249 "{$wgResourceBasePath}/resources/assets/licenses/", 250 $wgRightsIcon 251 ); 252} 253 254if ( isset( $wgFooterIcons['copyright']['copyright'] ) 255 && $wgFooterIcons['copyright']['copyright'] === [] 256) { 257 if ( $wgRightsIcon || $wgRightsText ) { 258 $wgFooterIcons['copyright']['copyright'] = [ 259 'url' => $wgRightsUrl, 260 'src' => $wgRightsIcon, 261 'alt' => $wgRightsText, 262 ]; 263 } 264} 265 266if ( isset( $wgFooterIcons['poweredby'] ) 267 && isset( $wgFooterIcons['poweredby']['mediawiki'] ) 268 && $wgFooterIcons['poweredby']['mediawiki']['src'] === null 269) { 270 $wgFooterIcons['poweredby']['mediawiki']['src'] = 271 "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"; 272 $wgFooterIcons['poweredby']['mediawiki']['srcset'] = 273 "$wgResourceBasePath/resources/assets/poweredby_mediawiki_132x47.png 1.5x, " . 274 "$wgResourceBasePath/resources/assets/poweredby_mediawiki_176x62.png 2x"; 275} 276 277/** 278 * Unconditional protection for NS_MEDIAWIKI since otherwise it's too easy for a 279 * sysadmin to set $wgNamespaceProtection incorrectly and leave the wiki insecure. 280 * 281 * Note that this is the definition of editinterface and it can be granted to 282 * all users if desired. 283 */ 284$wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface'; 285 286/** 287 * Initialise $wgLockManagers to include basic FS version 288 */ 289$wgLockManagers[] = [ 290 'name' => 'fsLockManager', 291 'class' => FSLockManager::class, 292 'lockDirectory' => "{$wgUploadDirectory}/lockdir", 293]; 294$wgLockManagers[] = [ 295 'name' => 'nullLockManager', 296 'class' => NullLockManager::class, 297]; 298 299/** 300 * Default parameters for the "<gallery>" tag. 301 * @see DefaultSettings.php for description of the fields. 302 */ 303$wgGalleryOptions += [ 304 'imagesPerRow' => 0, 305 'imageWidth' => 120, 306 'imageHeight' => 120, 307 'captionLength' => true, 308 'showBytes' => true, 309 'showDimensions' => true, 310 'mode' => 'traditional', 311]; 312 313/** 314 * Shortcuts for $wgLocalFileRepo 315 */ 316if ( !$wgLocalFileRepo ) { 317 $wgLocalFileRepo = [ 318 'class' => LocalRepo::class, 319 'name' => 'local', 320 'directory' => $wgUploadDirectory, 321 'scriptDirUrl' => $wgScriptPath, 322 'url' => $wgUploadBaseUrl ? $wgUploadBaseUrl . $wgUploadPath : $wgUploadPath, 323 'hashLevels' => $wgHashedUploadDirectory ? 2 : 0, 324 'thumbScriptUrl' => $wgThumbnailScriptPath, 325 'transformVia404' => !$wgGenerateThumbnailOnParse, 326 'deletedDir' => $wgDeletedDirectory, 327 'deletedHashLevels' => $wgHashedUploadDirectory ? 3 : 0 328 ]; 329} 330 331if ( !isset( $wgLocalFileRepo['backend'] ) ) { 332 // Create a default FileBackend name. 333 // FileBackendGroup will register a default, if absent from $wgFileBackends. 334 $wgLocalFileRepo['backend'] = $wgLocalFileRepo['name'] . '-backend'; 335} 336 337/** 338 * Shortcuts for $wgForeignFileRepos 339 */ 340if ( $wgUseSharedUploads ) { 341 if ( $wgSharedUploadDBname ) { 342 $wgForeignFileRepos[] = [ 343 'class' => ForeignDBRepo::class, 344 'name' => 'shared', 345 'directory' => $wgSharedUploadDirectory, 346 'url' => $wgSharedUploadPath, 347 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0, 348 'thumbScriptUrl' => $wgSharedThumbnailScriptPath, 349 'transformVia404' => !$wgGenerateThumbnailOnParse, 350 'dbType' => $wgDBtype, 351 'dbServer' => $wgDBserver, 352 'dbUser' => $wgDBuser, 353 'dbPassword' => $wgDBpassword, 354 'dbName' => $wgSharedUploadDBname, 355 'dbFlags' => ( $wgDebugDumpSql ? DBO_DEBUG : 0 ) | DBO_DEFAULT, 356 'tablePrefix' => $wgSharedUploadDBprefix, 357 'hasSharedCache' => $wgCacheSharedUploads, 358 'descBaseUrl' => $wgRepositoryBaseUrl, 359 'fetchDescription' => $wgFetchCommonsDescriptions, 360 ]; 361 } else { 362 $wgForeignFileRepos[] = [ 363 'class' => FileRepo::class, 364 'name' => 'shared', 365 'directory' => $wgSharedUploadDirectory, 366 'url' => $wgSharedUploadPath, 367 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0, 368 'thumbScriptUrl' => $wgSharedThumbnailScriptPath, 369 'transformVia404' => !$wgGenerateThumbnailOnParse, 370 'descBaseUrl' => $wgRepositoryBaseUrl, 371 'fetchDescription' => $wgFetchCommonsDescriptions, 372 ]; 373 } 374} 375if ( $wgUseInstantCommons ) { 376 $wgForeignFileRepos[] = [ 377 'class' => ForeignAPIRepo::class, 378 'name' => 'wikimediacommons', 379 'apibase' => 'https://commons.wikimedia.org/w/api.php', 380 'url' => 'https://upload.wikimedia.org/wikipedia/commons', 381 'thumbUrl' => 'https://upload.wikimedia.org/wikipedia/commons/thumb', 382 'hashLevels' => 2, 383 'transformVia404' => true, 384 'fetchDescription' => true, 385 'descriptionCacheExpiry' => 43200, 386 'apiThumbCacheExpiry' => 0, 387 ]; 388} 389foreach ( $wgForeignFileRepos as &$repo ) { 390 if ( !isset( $repo['directory'] ) && $repo['class'] === ForeignAPIRepo::class ) { 391 $repo['directory'] = $wgUploadDirectory; // b/c 392 } 393 if ( !isset( $repo['backend'] ) ) { 394 $repo['backend'] = $repo['name'] . '-backend'; 395 } 396} 397unset( $repo ); // no global pollution; destroy reference 398 399$rcMaxAgeDays = $wgRCMaxAge / ( 3600 * 24 ); 400// Ensure that default user options are not invalid, since that breaks Special:Preferences 401$wgDefaultUserOptions['rcdays'] = min( 402 $wgDefaultUserOptions['rcdays'], 403 ceil( $rcMaxAgeDays ) 404); 405$wgDefaultUserOptions['watchlistdays'] = min( 406 $wgDefaultUserOptions['watchlistdays'], 407 ceil( $rcMaxAgeDays ) 408); 409unset( $rcMaxAgeDays ); 410 411if ( !$wgCookiePrefix ) { 412 if ( $wgSharedDB && $wgSharedPrefix && in_array( 'user', $wgSharedTables ) ) { 413 $wgCookiePrefix = $wgSharedDB . '_' . $wgSharedPrefix; 414 } elseif ( $wgSharedDB && in_array( 'user', $wgSharedTables ) ) { 415 $wgCookiePrefix = $wgSharedDB; 416 } elseif ( $wgDBprefix ) { 417 $wgCookiePrefix = $wgDBname . '_' . $wgDBprefix; 418 } else { 419 $wgCookiePrefix = $wgDBname; 420 } 421} 422$wgCookiePrefix = strtr( $wgCookiePrefix, '=,; +."\'\\[', '__________' ); 423 424if ( $wgEnableEmail ) { 425 $wgUseEnotif = $wgEnotifUserTalk || $wgEnotifWatchlist; 426} else { 427 // Disable all other email settings automatically if $wgEnableEmail 428 // is set to false. - T65678 429 $wgAllowHTMLEmail = false; 430 $wgEmailAuthentication = false; // do not require auth if you're not sending email anyway 431 $wgEnableUserEmail = false; 432 $wgEnotifFromEditor = false; 433 $wgEnotifImpersonal = false; 434 $wgEnotifMaxRecips = 0; 435 $wgEnotifMinorEdits = false; 436 $wgEnotifRevealEditorAddress = false; 437 $wgEnotifUseRealName = false; 438 $wgEnotifUserTalk = false; 439 $wgEnotifWatchlist = false; 440 unset( $wgGroupPermissions['user']['sendemail'] ); 441 $wgUseEnotif = false; 442 $wgUserEmailUseReplyTo = false; 443 $wgUsersNotifiedOnAllChanges = []; 444} 445 446/** 447 * Definitions of the NS_ constants are in Defines.php 448 * @internal 449 */ 450$wgCanonicalNamespaceNames = NamespaceInfo::CANONICAL_NAMES; 451 452/// @todo UGLY UGLY 453if ( is_array( $wgExtraNamespaces ) ) { 454 $wgCanonicalNamespaceNames += $wgExtraNamespaces; 455} 456 457// Hard-deprecate setting $wgDummyLanguageCodes in LocalSettings.php 458if ( count( $wgDummyLanguageCodes ) !== 0 ) { 459 wfDeprecated( '$wgDummyLanguageCodes', '1.29' ); 460} 461// Merge in the legacy language codes, incorporating overrides from the config 462$wgDummyLanguageCodes += [ 463 // Internal language codes of the private-use area which get mapped to 464 // themselves. 465 'qqq' => 'qqq', // Used for message documentation 466 'qqx' => 'qqx', // Used for viewing message keys 467] + $wgExtraLanguageCodes + LanguageCode::getDeprecatedCodeMapping(); 468// Merge in (inverted) BCP 47 mappings 469foreach ( LanguageCode::getNonstandardLanguageCodeMapping() as $code => $bcp47 ) { 470 $bcp47 = strtolower( $bcp47 ); // force case-insensitivity 471 if ( !isset( $wgDummyLanguageCodes[$bcp47] ) ) { 472 $wgDummyLanguageCodes[$bcp47] = $wgDummyLanguageCodes[$code] ?? $code; 473 } 474} 475 476if ( $wgInvalidateCacheOnLocalSettingsChange ) { 477 Wikimedia\suppressWarnings(); 478 $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', filemtime( "$IP/LocalSettings.php" ) ) ); 479 Wikimedia\restoreWarnings(); 480} 481 482if ( $wgNewUserLog ) { 483 // Add new user log type 484 $wgLogTypes[] = 'newusers'; 485 $wgLogNames['newusers'] = 'newuserlogpage'; 486 $wgLogHeaders['newusers'] = 'newuserlogpagetext'; 487 $wgLogActionsHandlers['newusers/newusers'] = NewUsersLogFormatter::class; 488 $wgLogActionsHandlers['newusers/create'] = NewUsersLogFormatter::class; 489 $wgLogActionsHandlers['newusers/create2'] = NewUsersLogFormatter::class; 490 $wgLogActionsHandlers['newusers/byemail'] = NewUsersLogFormatter::class; 491 $wgLogActionsHandlers['newusers/autocreate'] = NewUsersLogFormatter::class; 492} 493 494if ( $wgPageCreationLog ) { 495 // Add page creation log type 496 $wgLogTypes[] = 'create'; 497 $wgLogActionsHandlers['create/create'] = LogFormatter::class; 498} 499 500if ( $wgPageLanguageUseDB ) { 501 $wgLogTypes[] = 'pagelang'; 502 $wgLogActionsHandlers['pagelang/pagelang'] = PageLangLogFormatter::class; 503} 504 505if ( $wgCookieSecure === 'detect' ) { 506 $wgCookieSecure = $wgForceHTTPS || ( WebRequest::detectProtocol() === 'https' ); 507} 508 509// Backwards compatibility with old password limits 510if ( $wgMinimalPasswordLength !== false ) { 511 $wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = $wgMinimalPasswordLength; 512} 513 514if ( $wgMaximalPasswordLength !== false ) { 515 $wgPasswordPolicy['policies']['default']['MaximalPasswordLength'] = $wgMaximalPasswordLength; 516} 517 518if ( $wgPHPSessionHandling !== 'enable' && 519 $wgPHPSessionHandling !== 'warn' && 520 $wgPHPSessionHandling !== 'disable' 521) { 522 $wgPHPSessionHandling = 'warn'; 523} 524if ( defined( 'MW_NO_SESSION' ) ) { 525 // If the entry point wants no session, force 'disable' here unless they 526 // specifically set it to the (undocumented) 'warn'. 527 $wgPHPSessionHandling = MW_NO_SESSION === 'warn' ? 'warn' : 'disable'; 528} 529 530MWDebug::setup(); 531 532// Reset the global service locator, so any services that have already been created will be 533// re-created while taking into account any custom settings and extensions. 534MediaWikiServices::resetGlobalInstance( new GlobalVarConfig(), 'quick' ); 535 536// Define a constant that indicates that the bootstrapping of the service locator 537// is complete. 538define( 'MW_SERVICE_BOOTSTRAP_COMPLETE', 1 ); 539 540MWExceptionHandler::installHandler(); 541 542// T30798: $wgServer must be explicitly set 543// @phan-suppress-next-line PhanSuspiciousValueComparisonInGlobalScope 544if ( $wgServer === false ) { 545 throw new FatalError( 546 '$wgServer must be set in LocalSettings.php. ' . 547 'See <a href="https://www.mediawiki.org/wiki/Manual:$wgServer">' . 548 'https://www.mediawiki.org/wiki/Manual:$wgServer</a>.' 549 ); 550} 551 552if ( $wgCanonicalServer === false ) { 553 $wgCanonicalServer = wfExpandUrl( $wgServer, PROTO_HTTP ); 554} 555 556// Set server name 557$serverParts = wfParseUrl( $wgCanonicalServer ); 558if ( $wgServerName !== false ) { 559 wfWarn( '$wgServerName should be derived from $wgCanonicalServer, ' 560 . 'not customized. Overwriting $wgServerName.' ); 561} 562$wgServerName = $serverParts['host']; 563unset( $serverParts ); 564 565// Set defaults for configuration variables 566// that are derived from the server name by default 567// Note: $wgEmergencyContact and $wgPasswordSender may be false or empty string (T104142) 568if ( !$wgEmergencyContact ) { 569 $wgEmergencyContact = 'wikiadmin@' . $wgServerName; 570} 571if ( !$wgPasswordSender ) { 572 $wgPasswordSender = 'apache@' . $wgServerName; 573} 574if ( !$wgNoReplyAddress ) { 575 $wgNoReplyAddress = $wgPasswordSender; 576} 577 578if ( $wgSecureLogin && substr( $wgServer, 0, 2 ) !== '//' ) { 579 $wgSecureLogin = false; 580 wfWarn( 'Secure login was enabled on a server that only supports ' 581 . 'HTTP or HTTPS. Disabling secure login.' ); 582} 583 584$wgVirtualRestConfig['global']['domain'] = $wgCanonicalServer; 585 586// Now that GlobalFunctions is loaded, set defaults that depend on it. 587if ( $wgTmpDirectory === false ) { 588 $wgTmpDirectory = wfTempDir(); 589} 590 591if ( $wgMainWANCache === false ) { 592 // Setup a WAN cache from $wgMainCacheType 593 $wgMainWANCache = 'mediawiki-main-default'; 594 $wgWANObjectCaches[$wgMainWANCache] = [ 595 'class' => WANObjectCache::class, 596 'cacheId' => $wgMainCacheType, 597 ]; 598} 599 600if ( $wgSharedDB && $wgSharedTables ) { 601 // Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections) 602 MediaWikiServices::getInstance()->getDBLoadBalancer()->setTableAliases( 603 array_fill_keys( 604 $wgSharedTables, 605 [ 606 'dbname' => $wgSharedDB, 607 'schema' => $wgSharedSchema, 608 'prefix' => $wgSharedPrefix 609 ] 610 ) 611 ); 612} 613 614// Raise the memory limit if it's too low 615// Note, this makes use of wfDebug, and thus should not be before 616// MWDebug::init() is called. 617wfMemoryLimit( $wgMemoryLimit ); 618 619/** 620 * Set up the timezone, suppressing the pseudo-security warning in PHP 5.1+ 621 * that happens whenever you use a date function without the timezone being 622 * explicitly set. Inspired by phpMyAdmin's treatment of the problem. 623 */ 624if ( $wgLocaltimezone === null ) { 625 Wikimedia\suppressWarnings(); 626 $wgLocaltimezone = date_default_timezone_get(); 627 Wikimedia\restoreWarnings(); 628} 629 630date_default_timezone_set( $wgLocaltimezone ); 631if ( $wgLocalTZoffset === null ) { 632 $wgLocalTZoffset = (int)date( 'Z' ) / 60; 633} 634// The part after the System| is ignored, but rest of MW fills it 635// out as the local offset. 636$wgDefaultUserOptions['timecorrection'] = "System|$wgLocalTZoffset"; 637 638if ( !$wgDBerrorLogTZ ) { 639 $wgDBerrorLogTZ = $wgLocaltimezone; 640} 641 642// Initialize the request object in $wgRequest 643$wgRequest = RequestContext::getMain()->getRequest(); // BackCompat 644// Set user IP/agent information for agent session consistency purposes 645$cpPosInfo = LBFactory::getCPInfoFromCookieValue( 646 // The cookie has no prefix and is set by MediaWiki::preOutputCommit() 647 $wgRequest->getCookie( 'cpPosIndex', '' ), 648 // Mitigate broken client-side cookie expiration handling (T190082) 649 time() - ChronologyProtector::POSITION_COOKIE_TTL 650); 651MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->setRequestInfo( [ 652 'IPAddress' => $wgRequest->getIP(), 653 'UserAgent' => $wgRequest->getHeader( 'User-Agent' ), 654 'ChronologyProtection' => $wgRequest->getHeader( 'MediaWiki-Chronology-Protection' ), 655 'ChronologyPositionIndex' => $wgRequest->getInt( 'cpPosIndex', $cpPosInfo['index'] ), 656 'ChronologyClientId' => $cpPosInfo['clientId'] 657 ?? $wgRequest->getHeader( 'MediaWiki-Chronology-Client-Id' ) 658] ); 659unset( $cpPosInfo ); 660// Make sure that object caching does not undermine the ChronologyProtector improvements 661if ( $wgRequest->getCookie( 'UseDC', '' ) === 'master' ) { 662 // The user is pinned to the primary DC, meaning that they made recent changes which should 663 // be reflected in their subsequent web requests. Avoid the use of interim cache keys because 664 // they use a blind TTL and could be stale if an object changes twice in a short time span. 665 MediaWikiServices::getInstance()->getMainWANObjectCache()->useInterimHoldOffCaching( false ); 666} 667 668// Useful debug output 669( function () { 670 global $wgCommandLineMode, $wgRequest; 671 $logger = LoggerFactory::getInstance( 'wfDebug' ); 672 if ( $wgCommandLineMode ) { 673 $self = $_SERVER['PHP_SELF'] ?? ''; 674 $logger->debug( "\n\nStart command line script $self" ); 675 } else { 676 $debug = "\n\nStart request {$wgRequest->getMethod()} {$wgRequest->getRequestURL()}\n"; 677 $debug .= "IP: " . $wgRequest->getIP() . "\n"; 678 $debug .= "HTTP HEADERS:\n"; 679 foreach ( $wgRequest->getAllHeaders() as $name => $value ) { 680 $debug .= "$name: $value\n"; 681 } 682 $debug .= "(end headers)"; 683 $logger->debug( $debug ); 684 } 685} )(); 686 687/** 688 * @var BagOStuff $wgMemc 689 * @deprecated since 1.35, use the LocalServerObjectCache service instead 690 */ 691$wgMemc = ObjectCache::getLocalClusterInstance(); 692 693// Most of the config is out, some might want to run hooks here. 694Hooks::runner()->onSetupAfterCache(); 695 696/** 697 * @var Language $wgContLang 698 * @deprecated since 1.32, use the ContentLanguage service directly 699 */ 700$wgContLang = MediaWikiServices::getInstance()->getContentLanguage(); 701 702// Now that variant lists may be available... 703$wgRequest->interpolateTitle(); 704 705/** 706 * @var MediaWiki\Session\SessionId|null The persistent session ID (if any) loaded at startup 707 */ 708$wgInitialSessionId = null; 709if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) { 710 // If session.auto_start is there, we can't touch session name 711 if ( $wgPHPSessionHandling !== 'disable' && !wfIniGetBool( 'session.auto_start' ) ) { 712 session_name( $wgSessionName ?: $wgCookiePrefix . '_session' ); 713 } 714 715 // Create the SessionManager singleton and set up our session handler, 716 // unless we're specifically asked not to. 717 if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) { 718 MediaWiki\Session\PHPSessionHandler::install( 719 MediaWiki\Session\SessionManager::singleton() 720 ); 721 } 722 723 // Initialize the session 724 try { 725 $session = MediaWiki\Session\SessionManager::getGlobalSession(); 726 } catch ( MediaWiki\Session\SessionOverflowException $ex ) { 727 // The exception is because the request had multiple possible 728 // sessions tied for top priority. Report this to the user. 729 $list = []; 730 foreach ( $ex->getSessionInfos() as $info ) { 731 $list[] = $info->getProvider()->describe( $wgContLang ); 732 } 733 $list = $wgContLang->listToText( $list ); 734 throw new HttpError( 400, 735 Message::newFromKey( 'sessionmanager-tie', $list )->inLanguage( $wgContLang )->plain() 736 ); 737 } 738 739 if ( $session->isPersistent() ) { 740 $wgInitialSessionId = $session->getSessionId(); 741 } 742 743 $session->renew(); 744 if ( MediaWiki\Session\PHPSessionHandler::isEnabled() && 745 ( $session->isPersistent() || $session->shouldRememberUser() ) && 746 session_id() !== $session->getId() 747 ) { 748 // Start the PHP-session for backwards compatibility 749 if ( session_id() !== '' ) { 750 wfDebugLog( 'session', 'PHP session {old_id} was already started, changing to {new_id}', 'all', [ 751 'old_id' => session_id(), 752 'new_id' => $session->getId(), 753 ] ); 754 session_write_close(); 755 } 756 session_id( $session->getId() ); 757 session_start(); 758 } 759 760 unset( $session ); 761} else { 762 // Even if we didn't set up a global Session, still install our session 763 // handler unless specifically requested not to. 764 if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) { 765 MediaWiki\Session\PHPSessionHandler::install( 766 MediaWiki\Session\SessionManager::singleton() 767 ); 768 } 769} 770 771/** 772 * @var User $wgUser 773 * @deprecated since 1.35, use an available context source when possible, or, as a backup, 774 * RequestContext::getMain() 775 */ 776$wgUser = RequestContext::getMain()->getUser(); // BackCompat 777 778/** 779 * @var Language $wgLang 780 */ 781$wgLang = new StubUserLang; 782 783/** 784 * @var OutputPage $wgOut 785 */ 786$wgOut = RequestContext::getMain()->getOutput(); // BackCompat 787 788/** 789 * @var Parser $wgParser 790 * @deprecated since 1.32, use MediaWikiServices::getInstance()->getParser() instead 791 */ 792$wgParser = new DeprecatedGlobal( 'wgParser', function () { 793 return MediaWikiServices::getInstance()->getParser(); 794}, '1.32' ); 795 796/** 797 * @var Title $wgTitle 798 */ 799$wgTitle = null; 800 801// Extension setup functions 802// Entries should be added to this variable during the inclusion 803// of the extension file. This allows the extension to perform 804// any necessary initialisation in the fully initialised environment 805foreach ( $wgExtensionFunctions as $func ) { 806 call_user_func( $func ); 807} 808 809// If the session user has a 0 id but a valid name, that means we need to 810// autocreate it. 811if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) { 812 $sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser(); 813 if ( $sessionUser->getId() === 0 && User::isValidUserName( $sessionUser->getName() ) ) { 814 $res = MediaWikiServices::getInstance()->getAuthManager()->autoCreateUser( 815 $sessionUser, 816 MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION, 817 true 818 ); 819 \MediaWiki\Logger\LoggerFactory::getInstance( 'authevents' )->info( 'Autocreation attempt', [ 820 'event' => 'autocreate', 821 'status' => strval( $res ), 822 ] ); 823 unset( $res ); 824 } 825 unset( $sessionUser ); 826} 827 828if ( !$wgCommandLineMode ) { 829 Pingback::schedulePingback(); 830} 831 832$wgFullyInitialised = true; 833