Setup.php 31 KB

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