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