Setup.php 30 KB

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