Merge "Make statsd sampling rates configurable"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 5 Sep 2016 08:52:54 +0000 (08:52 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 5 Sep 2016 08:52:54 +0000 (08:52 +0000)
1  2 
includes/DefaultSettings.php
includes/GlobalFunctions.php

@@@ -75,7 -75,7 +75,7 @@@ $wgConfigRegistry = 
   * MediaWiki version number
   * @since 1.2
   */
 -$wgVersion = '1.27.0-alpha';
 +$wgVersion = '1.28.0-alpha';
  
  /**
   * Name of the site. It must be changed in LocalSettings.php
@@@ -284,10 -284,10 +284,10 @@@ $wgLogo = false
   *
   * @par Example:
   * @code
 - * $wgLogoHD = array(
 + * $wgLogoHD = [
   *    "1.5x" => "path/to/1.5x_version.png",
   *    "2x" => "path/to/2x_version.png"
 - * );
 + * ];
   * @endcode
   *
   * @since 1.25
@@@ -398,13 -398,9 +398,13 @@@ $wgAllowImageMoving = true
  $wgEnableAsyncUploads = false;
  
  /**
 - * These are additional characters that should be replaced with '-' in filenames
 + * Additional characters that are not allowed in filenames. They are replaced with '-' when
 + * uploading. Like $wgLegalTitleChars, this is a regexp character class.
 + *
 + * Slashes and backslashes are disallowed regardless of this setting, but included here for
 + * completeness.
   */
 -$wgIllegalFileChars = ":";
 +$wgIllegalFileChars = ":\\/\\\\";
  
  /**
   * What directory to place deleted uploads in.
@@@ -533,74 -529,11 +533,74 @@@ $wgUseInstantCommons = false
   * Array of foreign file repo names (set in $wgForeignFileRepos above) that
   * are allowable upload targets. These wikis must have some method of
   * authentication (i.e. CentralAuth), and be CORS-enabled for this wiki.
 + * The string 'local' signifies the default local file repository.
   *
   * Example:
 - * $wgForeignUploadTargets = array( 'shared' );
 + * $wgForeignUploadTargets = [ 'shared' ];
   */
 -$wgForeignUploadTargets = [];
 +$wgForeignUploadTargets = [ 'local' ];
 +
 +/**
 + * Configuration for file uploads using the embeddable upload dialog
 + * (https://www.mediawiki.org/wiki/Upload_dialog).
 + *
 + * This applies also to foreign uploads to this wiki (the configuration is loaded by remote wikis
 + * using the action=query&meta=siteinfo API).
 + *
 + * See below for documentation of each property. None of the properties may be omitted.
 + */
 +$wgUploadDialog = [
 +      // Fields to make available in the dialog. `true` means that this field is visible, `false` means
 +      // that it is hidden. The "Name" field can't be hidden. Note that you also have to add the
 +      // matching replacement to the 'filepage' format key below to make use of these.
 +      'fields' => [
 +              'description' => true,
 +              'date' => false,
 +              'categories' => false,
 +      ],
 +      // Suffix of localisation messages used to describe the license under which the uploaded file will
 +      // be released. The same value may be set for both 'local' and 'foreign' uploads.
 +      'licensemessages' => [
 +              // The 'local' messages are used for local uploads on this wiki:
 +              // * upload-form-label-own-work-message-generic-local
 +              // * upload-form-label-not-own-work-message-generic-local
 +              // * upload-form-label-not-own-work-local-generic-local
 +              'local' => 'generic-local',
 +              // The 'foreign' messages are used for cross-wiki uploads from other wikis to this wiki:
 +              // * upload-form-label-own-work-message-generic-foreign
 +              // * upload-form-label-not-own-work-message-generic-foreign
 +              // * upload-form-label-not-own-work-local-generic-foreign
 +              'foreign' => 'generic-foreign',
 +      ],
 +      // Upload comments to use for 'local' and 'foreign' uploads. This can also be set to a single
 +      // string value, in which case it is used for both kinds of uploads. Available replacements:
 +      // * $HOST - domain name from which a cross-wiki upload originates
 +      // * $PAGENAME - wiki page name from which an upload originates
 +      'comment' => [
 +              'local' => '',
 +              'foreign' => '',
 +      ],
 +      // Format of the file page wikitext to be generated from the fields input by the user.
 +      'format' => [
 +              // Wrapper for the whole page. Available replacements:
 +              // * $DESCRIPTION - file description, as input by the user (only if the 'description' field is
 +              //   enabled), wrapped as defined below in the 'description' key
 +              // * $DATE - file creation date, as input by the user (only if the 'date' field is enabled)
 +              // * $SOURCE - as defined below in the 'ownwork' key, may be extended in the future
 +              // * $AUTHOR - linked user name, may be extended in the future
 +              // * $LICENSE - as defined below in the 'license' key, may be extended in the future
 +              // * $CATEGORIES - file categories wikitext, as input by the user (only if the 'categories'
 +              //   field is enabled), or if no input, as defined below in the 'uncategorized' key
 +              'filepage' => '$DESCRIPTION',
 +              // Wrapped for file description. Available replacements:
 +              // * $LANGUAGE - source wiki's content language
 +              // * $TEXT - input by the user
 +              'description' => '$TEXT',
 +              'ownwork' => '',
 +              'license' => '',
 +              'uncategorized' => '',
 +      ],
 +];
  
  /**
   * File backend structure configuration.
@@@ -685,7 -618,7 +685,7 @@@ $wgUseSharedUploads = false
  /**
   * Full path on the web server where shared uploads can be found
   */
 -$wgSharedUploadPath = "http://commons.wikimedia.org/shared/images";
 +$wgSharedUploadPath = null;
  
  /**
   * Fetch commons image description pages and display them on the local wiki?
@@@ -695,7 -628,7 +695,7 @@@ $wgFetchCommonsDescriptions = false
  /**
   * Path on the file system where shared uploads can be found.
   */
 -$wgSharedUploadDirectory = "/var/www/wiki3/images";
 +$wgSharedUploadDirectory = null;
  
  /**
   * DB name with metadata about shared directory.
@@@ -759,10 -692,10 +759,10 @@@ $wgCopyUploadTimeout = false
   *
   * @par Example:
   * @code
 - * $wgMaxUploadSize = array(
 + * $wgMaxUploadSize = [
   *     '*' => 250 * 1024,
   *     'url' => 500 * 1024,
 - * );
 + * ];
   * @endcode
   * Sets the maximum for all uploads to 250 kB except for upload-by-url, which
   * will have a maximum of 500 kB.
@@@ -945,11 -878,22 +945,11 @@@ $wgTrustedMediaFormats = 
  /**
   * Plugins for media file type handling.
   * Each entry in the array maps a MIME type to a class name
 + *
 + * Core media handlers are listed in MediaHandlerFactory,
 + * and extensions should use extension.json.
   */
 -$wgMediaHandlers = [
 -      'image/jpeg' => 'JpegHandler',
 -      'image/png' => 'PNGHandler',
 -      'image/gif' => 'GIFHandler',
 -      'image/tiff' => 'TiffHandler',
 -      'image/webp' => 'WebPHandler',
 -      'image/x-ms-bmp' => 'BmpHandler',
 -      'image/x-bmp' => 'BmpHandler',
 -      'image/x-xcf' => 'XCFHandler',
 -      'image/svg+xml' => 'SvgHandler', // official
 -      'image/svg' => 'SvgHandler', // compat
 -      'image/vnd.djvu' => 'DjVuHandler', // official
 -      'image/x.djvu' => 'DjVuHandler', // compat
 -      'image/x-djvu' => 'DjVuHandler', // compat
 -];
 +$wgMediaHandlers = [];
  
  /**
   * Plugins for page content model handling.
@@@ -1036,27 -980,6 +1036,27 @@@ $wgCustomConvertCommand = false
   */
  $wgJpegTran = '/usr/bin/jpegtran';
  
 +/**
 + * At default setting of 'yuv420', JPEG thumbnails will use 4:2:0 chroma
 + * subsampling to reduce file size, at the cost of possible color fringing
 + * at sharp edges.
 + *
 + * See https://en.wikipedia.org/wiki/Chroma_subsampling
 + *
 + * Supported values:
 + *   false - use scaling system's default (same as pre-1.27 behavior)
 + *   'yuv444' - luma and chroma at same resolution
 + *   'yuv422' - chroma at 1/2 resolution horizontally, full vertically
 + *   'yuv420' - chroma at 1/2 resolution in both dimensions
 + *
 + * This setting is currently supported only for the ImageMagick backend;
 + * others may default to 4:2:0 or 4:4:4 or maintaining the source file's
 + * sampling in the thumbnail.
 + *
 + * @since 1.27
 + */
 +$wgJpegPixelFormat = 'yuv420';
 +
  /**
   * Some tests and extensions use exiv2 to manipulate the Exif metadata in some
   * image formats.
@@@ -1156,9 -1079,9 +1156,9 @@@ $wgMaxAnimatedGifArea = 1.25e7
   * @par Example:
   * @code
   *  // PNG is lossless, but inefficient for photos
 - *  $wgTiffThumbnailType = array( 'png', 'image/png' );
 + *  $wgTiffThumbnailType = [ 'png', 'image/png' ];
   *  // JPEG is good for photos, but has no transparency support. Bad for diagrams.
 - *  $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' );
 + *  $wgTiffThumbnailType = [ 'jpg', 'image/jpeg' ];
   * @endcode
   */
  $wgTiffThumbnailType = false;
@@@ -1314,7 -1237,7 +1314,7 @@@ $wgTrivialMimeDetection = false
  
  /**
   * Additional XML types we can allow via MIME-detection.
 - * array = ( 'rootElement' => 'associatedMimeType' )
 + * array = [ 'rootElement' => 'associatedMimeType' ]
   */
  $wgXMLMimeTypes = [
        'http://www.w3.org/2000/svg:svg' => 'image/svg+xml',
@@@ -1371,7 -1294,7 +1371,7 @@@ $wgThumbnailBuckets = null
   * needs in order to be used as the reference for a given thumbnail. For example, with the
   * following buckets:
   *
 - * $wgThumbnailBuckets = array ( 128, 256, 512 );
 + * $wgThumbnailBuckets = [ 128, 256, 512 ];
   *
   * and a distance of 50:
   *
@@@ -1437,10 -1360,7 +1437,10 @@@ $wgGalleryOptions = 
        'imagesPerRow' => 0, // Default number of images per-row in the gallery. 0 -> Adapt to screensize
        'imageWidth' => 120, // Width of the cells containing images in galleries (in "px")
        'imageHeight' => 120, // Height of the cells containing images in galleries (in "px")
 -      'captionLength' => 25, // Length of caption to truncate (in characters)
 +      'captionLength' => true, // Deprecated @since 1.28
 +                               // Length to truncate filename to in caption when using "showfilename".
 +                               // A value of 'true' will truncate the filename to one line using CSS
 +                               // and will be the behaviour after deprecation.
        'showBytes' => true, // Show the filesize in bytes in categories
        'mode' => 'traditional',
  ];
@@@ -1620,14 -1540,14 +1620,14 @@@ $wgPasswordExpireGrace = 3600 * 24 * 7
   * Default to false or fill an array :
   *
   * @code
 - * $wgSMTP = array(
 + * $wgSMTP = [
   *     'host'     => 'SMTP domain',
   *     'IDHost'   => 'domain for MessageID',
   *     'port'     => '25',
   *     'auth'     => [true|false],
   *     'username' => [SMTP username],
   *     'password' => [SMTP password],
 - * );
 + * ];
   * @endcode
   */
  $wgSMTP = false;
@@@ -1670,9 -1590,6 +1670,9 @@@ $wgEnotifWatchlist = false
  /**
   * Allow users to enable email notification ("enotif") when someone edits their
   * user talk page.
 + *
 + * The owner of the user talk page must also have the 'enotifusertalkpages' user
 + * preference set to true.
   */
  $wgEnotifUserTalk = false;
  
  $wgEnotifRevealEditorAddress = false;
  
  /**
 - * Send notification mails on minor edits to watchlist pages. This is enabled
 - * by default. User talk notifications are affected by this, $wgEnotifUserTalk, and
 - * the nominornewtalk user right.
 + * Potentially send notification mails on minor edits to pages. This is enabled
 + * by default.  If this is false, users will never be notified on minor edits.
 + *
 + * If it is true, editors with the 'nominornewtalk' right (typically bots) will still not
 + * trigger notifications for minor edits they make (to any page, not just user talk).
 + *
 + * Finally, if the watcher/recipient has the 'enotifminoredits' user preference set to
 + * false, they will not receive notifications for minor edits.
 + *
 + * User talk notifications are also affected by $wgEnotifMinorEdits, the above settings,
 + * $wgEnotifUserTalk, and the preference described there.
   */
  $wgEnotifMinorEdits = true;
  
@@@ -1904,7 -1813,7 +1904,7 @@@ $wgSharedSchema = false
   *                  to several groups, the most specific group defined here is used.
   *
   *   - flags:       bit field
 - *                  - DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
 + *                  - DBO_DEFAULT -- turns on DBO_TRX only if "cliMode" is off (recommended)
   *                  - DBO_DEBUG -- equivalent of $wgDebugDumpSql
   *                  - DBO_TRX -- wrap entire request in a transaction
   *                  - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
   *                                    if available
   *
   *   - max lag:     (optional) Maximum replication lag before a slave will taken out of rotation
 + *   - is static:   (optional) Set to true if the dataset is static and no replication is used.
 + *   - cliMode:     (optional) Connection handles will not assume that requests are short-lived
 + *                  nor that INSERT..SELECT can be rewritten into a buffered SELECT and INSERT.
 + *                  [Default: uses value of $wgCommandLineMode]
   *
   *   These and any other user-defined properties will be assigned to the mLBInfo member
   *   variable of the Database object.
@@@ -2078,7 -1983,7 +2078,7 @@@ $wgCompressRevisions = false
   *
   * Short names of ExternalStore classes may be specified in an array here:
   * @code
 - * $wgExternalStores = array("http","file","custom")...
 + * $wgExternalStores = [ "http","file","custom" ]...
   * @endcode
   *
   * CAUTION: Access to database might lead to code execution
@@@ -2091,9 -1996,9 +2091,9 @@@ $wgExternalStores = []
   * @par Example:
   * Create a cluster named 'cluster1' containing three servers:
   * @code
 - * $wgExternalServers = array(
 - *     'cluster1' => array( 'srv28', 'srv29', 'srv30' )
 - * );
 + * $wgExternalServers = [
 + *     'cluster1' => [ 'srv28', 'srv29', 'srv30' ]
 + * ];
   * @endcode
   *
   * Used by LBFactorySimple, may be ignored if $wgLBFactoryConf is set to
@@@ -2110,7 -2015,7 +2110,7 @@@ $wgExternalServers = []
   *
   * @par Example:
   * @code
 - * $wgDefaultExternalStore = array( 'DB://cluster1', 'DB://cluster2' );
 + * $wgDefaultExternalStore = [ 'DB://cluster1', 'DB://cluster2' ];
   * @endcode
   *
   * @var array
@@@ -2264,7 -2169,7 +2264,7 @@@ $wgLanguageConverterCacheType = CACHE_A
   * given, giving a callable function which will generate a suitable cache object.
   */
  $wgObjectCaches = [
 -      CACHE_NONE => [ 'class' => 'EmptyBagOStuff' ],
 +      CACHE_NONE => [ 'class' => 'EmptyBagOStuff', 'reportDupes' => false ],
        CACHE_DB => [ 'class' => 'SqlBagOStuff', 'loggroup' => 'SQLBagOStuff' ],
  
        CACHE_ANYTHING => [ 'factory' => 'ObjectCache::newAnything' ],
                'loggroup'  => 'SQLBagOStuff'
        ],
  
 -      'apc' => [ 'class' => 'APCBagOStuff' ],
 -      'xcache' => [ 'class' => 'XCacheBagOStuff' ],
 -      'wincache' => [ 'class' => 'WinCacheBagOStuff' ],
 +      'apc' => [ 'class' => 'APCBagOStuff', 'reportDupes' => false ],
 +      'xcache' => [ 'class' => 'XCacheBagOStuff', 'reportDupes' => false ],
 +      'wincache' => [ 'class' => 'WinCacheBagOStuff', 'reportDupes' => false ],
        'memcached-php' => [ 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ],
        'memcached-pecl' => [ 'class' => 'MemcachedPeclBagOStuff', 'loggroup' => 'memcached' ],
 -      'hash' => [ 'class' => 'HashBagOStuff' ],
 +      'hash' => [ 'class' => 'HashBagOStuff', 'reportDupes' => false ],
  ];
  
  /**
@@@ -2318,24 -2223,27 +2318,24 @@@ $wgMainWANCache = false
   *
   * The format is an associative array where the key is a cache identifier, and
   * the value is an associative array of parameters. The "cacheId" parameter is
 - * a cache identifier from $wgObjectCaches. The "relayerConfig" parameter is an
 - * array used to construct an EventRelayer object. The "pool" parameter is a
 - * string that is used as a PubSub channel prefix. The "loggroup" parameter
 - * controls where log events are sent.
 + * a cache identifier from $wgObjectCaches. The "channels" parameter is a map of
 + * actions ('purge') to PubSub channels defined in $wgEventRelayerConfig.
 + * The "loggroup" parameter controls where log events are sent.
   *
   * @since 1.26
   */
  $wgWANObjectCaches = [
        CACHE_NONE => [
 -              'class'         => 'WANObjectCache',
 -              'cacheId'       => CACHE_NONE,
 -              'pool'          => 'mediawiki-main-none',
 -              'relayerConfig' => [ 'class' => 'EventRelayerNull' ]
 +              'class'    => 'WANObjectCache',
 +              'cacheId'  => CACHE_NONE,
 +              'channels' => []
        ]
        /* Example of a simple single data-center cache:
 -      'memcached-php' => array(
 -              'class'         => 'WANObjectCache',
 -              'cacheId'       => 'memcached-php',
 -              'pool'          => 'mediawiki-main-memcached',
 -              'relayerConfig' => array( 'class' => 'EventRelayerNull' )
 -      )
 +      'memcached-php' => [
 +              'class'    => 'WANObjectCache',
 +              'cacheId'  => 'memcached-php',
 +              'channels' => [ 'purge' => 'wancache-main-memcached-purge' ]
 +      ]
        */
  ];
  
@@@ -2401,13 -2309,6 +2401,13 @@@ $wgSessionHandler = null
   */
  $wgPHPSessionHandling = 'enable';
  
 +/**
 + * Number of internal PBKDF2 iterations to use when deriving session secrets.
 + *
 + * @since 1.28
 + */
 +$wgSessionPbkdf2Iterations = 10001;
 +
  /**
   * If enabled, will send MemCached debugging information to $wgDebugLogFile
   */
@@@ -2527,7 -2428,7 +2527,7 @@@ $wgFileCacheDepth = 2
  
  /**
   * Kept for extension compatibility; see $wgParserCacheType
 - * @deprecated 1.26
 + * @deprecated since 1.26
   */
  $wgEnableParserCache = true;
  
@@@ -2561,6 -2462,12 +2561,6 @@@ $wgSidebarCacheExpiry = 86400
   */
  $wgUseGzip = false;
  
 -/**
 - * Whether MediaWiki should send an ETag header. Seems to cause
 - * broken behavior with Squid 2.6, see bug 7098.
 - */
 -$wgUseETag = false;
 -
  /**
   * Clock skew or the one-second resolution of time() can occasionally cause cache
   * problems when the user requests two pages within a short period of time. This
@@@ -2682,13 -2589,6 +2682,13 @@@ $wgCdnMaxageLagged = 30
   */
  $wgCdnReboundPurgeDelay = 0;
  
 +/**
 + * Cache timeout for the CDN when a response is known to be wrong or incomplete (due to load)
 + * @see $wgSquidMaxage
 + * @since 1.27
 + */
 +$wgCdnMaxageSubstitute = 60;
 +
  /**
   * Default maximum age for raw CSS/JS accesses
   *
@@@ -2748,16 -2648,16 +2748,16 @@@ $wgSquidPurgeUseHostHeader = true
   * @par Example configuration to send purges for upload.wikimedia.org to one
   * multicast group and all other purges to another:
   * @code
 - * $wgHTCPRouting = array(
 - *         '|^https?://upload\.wikimedia\.org|' => array(
 + * $wgHTCPRouting = [
 + *         '|^https?://upload\.wikimedia\.org|' => [
   *                 'host' => '239.128.0.113',
   *                 'port' => 4827,
 - *         ),
 - *         '' => array(
 + *         ],
 + *         '' => [
   *                 'host' => '239.128.0.112',
   *                 'port' => 4827,
 - *         ),
 - * );
 + *         ],
 + * ];
   * @endcode
   *
   * You can also pass an array of hosts to send purges too. This is useful when
   *
   * @par Example of sending purges to multiple hosts:
   * @code
 - * $wgHTCPRouting = array(
 - *     '' => array(
 + * $wgHTCPRouting = [
 + *     '' => [
   *         // Purges to text caches using multicast
 - *         array( 'host' => '239.128.0.114', 'port' => '4827' ),
 + *         [ 'host' => '239.128.0.114', 'port' => '4827' ],
   *         // Purges to a hardcoded list of caches
 - *         array( 'host' => '10.88.66.1', 'port' => '4827' ),
 - *         array( 'host' => '10.88.66.2', 'port' => '4827' ),
 - *         array( 'host' => '10.88.66.3', 'port' => '4827' ),
 - *     ),
 - * );
 + *         [ 'host' => '10.88.66.1', 'port' => '4827' ],
 + *         [ 'host' => '10.88.66.2', 'port' => '4827' ],
 + *         [ 'host' => '10.88.66.3', 'port' => '4827' ],
 + *     ],
 + * ];
   * @endcode
   *
   * @since 1.22
@@@ -2892,6 -2792,15 +2892,6 @@@ $wgDummyLanguageCodes = 
        'zh-yue' => 'yue',
  ];
  
 -/**
 - * Character set for use in the article edit box. Language-specific encodings
 - * may be defined.
 - *
 - * This historic feature is one of the first that was added by former MediaWiki
 - * team leader Brion Vibber, and is used to support the Esperanto x-system.
 - */
 -$wgEditEncoding = '';
 -
  /**
   * Set this to true to replace Arabic presentation forms with their standard
   * forms in the U+0600-U+06FF block. This only works if $wgLanguageCode is
@@@ -3089,7 -2998,7 +3089,7 @@@ $wgLoginLanguageSelector = false
   * To allow language-specific main page and community
   * portal:
   * @code
 - *     $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' );
 + *     $wgForceUIMsgAsContentMsg = [ 'mainpage', 'portal-url' ];
   * @endcode
   */
  $wgForceUIMsgAsContentMsg = [];
@@@ -3197,13 -3106,22 +3197,13 @@@ $wgHTMLFormAllowTableFormat = true
  $wgUseMediaWikiUIEverywhere = false;
  
  /**
 - * Should we try to make our HTML output well-formed XML?  If set to false,
 - * output will be a few bytes shorter, and the HTML will arguably be more
 - * readable.  If set to true, life will be much easier for the authors of
 - * screen-scraping bots, and the HTML will arguably be more readable.
 - *
 - * Setting this to false may omit quotation marks on some attributes, omit
 - * slashes from some self-closing tags, omit some ending tags, etc., where
 - * permitted by HTML5.  Setting it to true will not guarantee that all pages
 - * will be well-formed, although non-well-formed pages should be rare and it's
 - * a bug if you find one.  Conversely, setting it to false doesn't mean that
 - * all XML-y constructs will be omitted, just that they might be.
 + * Whether to label the store-to-database-and-show-to-others button in the editor
 + * as "Save page"/"Save changes" if false (the default) or, if true, instead as
 + * "Publish page"/"Publish changes".
   *
 - * Because of compatibility with screen-scraping bots, and because it's
 - * controversial, this is currently left to true by default.
 + * @since 1.28
   */
 -$wgWellFormedXml = true;
 +$wgEditSubmitButtonLabelPublish = false;
  
  /**
   * Permit other namespaces in addition to the w3.org default.
@@@ -3468,13 -3386,13 +3468,13 @@@ $wgMangleFlashPolicy = true
   *
   * @par Example:
   * @code
 - *   $wgResourceModules['ext.myExtension'] = array(
 + *   $wgResourceModules['ext.myExtension'] = [
   *      'scripts' => 'myExtension.js',
   *      'styles' => 'myExtension.css',
 - *      'dependencies' => array( 'jquery.cookie', 'jquery.tabIndex' ),
 + *      'dependencies' => [ 'jquery.cookie', 'jquery.tabIndex' ],
   *      'localBasePath' => __DIR__,
   *      'remoteExtPath' => 'MyExtension',
 - *   );
 + *   ];
   * @endcode
   */
  $wgResourceModules = [];
   *
   * @par Example:
   * @code
 - *   $wgResourceModules['bar'] = array(
 + *   $wgResourceModules['bar'] = [
   *     'scripts' => 'resources/bar/bar.js',
   *     'styles' => 'resources/bar/main.css',
 - *   );
 + *   ];
   *
 - *   $wgResourceModuleSkinStyles['foo'] = array(
 + *   $wgResourceModuleSkinStyles['foo'] = [
   *     'bar' => 'skins/Foo/bar.css',
 - *   );
 + *   ];
   * @endcode
   *
   * This is mostly equivalent to:
   *
   * @par Equivalent:
   * @code
 - *   $wgResourceModules['bar'] = array(
 + *   $wgResourceModules['bar'] = [
   *     'scripts' => 'resources/bar/bar.js',
   *     'styles' => 'resources/bar/main.css',
 - *     'skinStyles' => array(
 + *     'skinStyles' => [
   *       'foo' => skins/Foo/bar.css',
 - *     ),
 - *   );
 + *     ],
 + *   ];
   * @endcode
   *
   * If the module already defines its own entry in `skinStyles` for a given skin, then
   *
   * @par Example:
   * @code
 - *   $wgResourceModules['bar'] = array(
 + *   $wgResourceModules['bar'] = [
   *     'scripts' => 'resources/bar/bar.js',
   *     'styles' => 'resources/bar/basic.css',
 - *     'skinStyles' => array(
 - *       'default' => 'resources/bar/additional.css',
 - *     ),
 - *   );
 + *     'skinStyles' => [
 + *      'default' => 'resources/bar/additional.css',
 + *     ],
 + *   ];
   *   // Note the '+' character:
 - *   $wgResourceModuleSkinStyles['foo'] = array(
 + *   $wgResourceModuleSkinStyles['foo'] = [
   *     '+bar' => 'skins/Foo/bar.css',
 - *   );
 + *   ];
   * @endcode
   *
   * This is mostly equivalent to:
   *
   * @par Equivalent:
   * @code
 - *   $wgResourceModules['bar'] = array(
 + *   $wgResourceModules['bar'] = [
   *     'scripts' => 'resources/bar/bar.js',
   *     'styles' => 'resources/bar/basic.css',
 - *     'skinStyles' => array(
 + *     'skinStyles' => [
   *       'default' => 'resources/bar/additional.css',
 - *       'foo' => array(
 + *       'foo' => [
   *         'resources/bar/additional.css',
   *         'skins/Foo/bar.css',
 - *       ),
 - *     ),
 - *   );
 + *       ],
 + *     ],
 + *   ];
   * @endcode
   *
   * In other words, as a module author, use the `styles` list for stylesheets that may not be
   *
   * @par Example:
   * @code
 - *   $wgResourceModuleSkinStyles['foo'] = array(
 + *   $wgResourceModuleSkinStyles['foo'] = [
   *     'bar' => 'bar.css',
   *     'quux' => 'quux.css',
   *     'remoteSkinPath' => 'Foo',
   *     'localBasePath' => __DIR__,
 - *   );
 + *   ];
   * @endcode
   */
  $wgResourceModuleSkinStyles = [];
@@@ -3721,11 -3639,11 +3721,11 @@@ $wgResourceLoaderValidateStaticJS = fal
   *
   * @par Example:
   * @code
 - *   $wgResourceLoaderLESSVars = array(
 + *   $wgResourceLoaderLESSVars = [
   *     'baseFontSize'  => '1em',
   *     'smallFontSize' => '0.75em',
   *     'WikimediaBlue' => '#006699',
 - *   );
 + *   ];
   * @endcode
   * @since 1.22
   */
@@@ -3759,8 -3677,10 +3759,8 @@@ $wgResourceLoaderLESSImportPaths = 
  /**
   * Whether ResourceLoader should attempt to persist modules in localStorage on
   * browsers that support the Web Storage API.
 - *
 - * @since 1.23 - Client-side module persistence is experimental. Exercise care.
   */
 -$wgResourceLoaderStorageEnabled = false;
 +$wgResourceLoaderStorageEnabled = true;
  
  /**
   * Cache version for client-side ResourceLoader module storage. You can trigger
@@@ -3821,12 -3741,12 +3821,12 @@@ $wgMetaNamespaceTalk = false
   *
   * @par Example:
   * @code
 - * $wgExtraNamespaces = array(
 + * $wgExtraNamespaces = [
   *    100 => "Hilfe",
   *    101 => "Hilfe_Diskussion",
   *    102 => "Aide",
   *    103 => "Discussion_Aide"
 - * );
 + * ];
   * @endcode
   *
   * @todo Add a note about maintenance/namespaceDupes.php
@@@ -3853,10 -3773,10 +3853,10 @@@ $wgExtraGenderNamespaces = []
   *
   * @par Example:
   * @code
 - *    $wgNamespaceAliases = array(
 + *    $wgNamespaceAliases = [
   *        'Wikipedian' => NS_USER,
   *        'Help' => 100,
 - *    );
 + *    ];
   * @endcode
   */
  $wgNamespaceAliases = [];
@@@ -4183,7 -4103,7 +4183,7 @@@ $wgAllowExternalImages = false
   * @par Examples:
   * @code
   * $wgAllowExternalImagesFrom = 'http://127.0.0.1/';
 - * $wgAllowExternalImagesFrom = array( 'http://127.0.0.1/', 'http://example.com' );
 + * $wgAllowExternalImagesFrom = [ 'http://127.0.0.1/', 'http://example.com' ];
   * @endcode
   */
  $wgAllowExternalImagesFrom = '';
@@@ -4224,8 -4144,6 +4224,8 @@@ $wgAllowImageTag = false
   *    - RaggettInternalHHVM: Use the limited-functionality HHVM extension
   *    - RaggettInternalPHP: Use the PECL extension
   *    - RaggettExternal: Shell out to an external binary (tidyBin)
 + *    - Html5Depurate: Use external Depurate service
 + *    - Html5Internal: Use the built-in HTML5 balancer
   *
   *  - tidyConfigFile: Path to configuration file for any of the Raggett drivers
   *  - debugComment: True to add a comment to the output with warning messages
@@@ -4278,13 -4196,7 +4278,13 @@@ $wgDebugTidy = false
  $wgRawHtml = false;
  
  /**
 - * Set a default target for external links, e.g. _blank to pop up a new window
 + * Set a default target for external links, e.g. _blank to pop up a new window.
 + *
 + * This will also set the "noreferrer" and "noopener" link rel to prevent the
 + * attack described at https://mathiasbynens.github.io/rel-noopener/ .
 + * Some older browsers may not support these link attributes, hence
 + * setting $wgExternalLinkTarget to _blank may represent a security risk
 + * to some of your users.
   */
  $wgExternalLinkTarget = false;
  
@@@ -4307,7 -4219,8 +4307,7 @@@ $wgNoFollowNsExceptions = []
   * (or any subdomains) will not be set to rel="nofollow" regardless of the
   * value of $wgNoFollowLinks.  For instance:
   *
 - * $wgNoFollowDomainExceptions = array( 'en.wikipedia.org', 'wiktionary.org',
 - * 'mediawiki.org' );
 + * $wgNoFollowDomainExceptions = [ 'en.wikipedia.org', 'wiktionary.org', 'mediawiki.org' ];
   *
   * This would add rel="nofollow" to links to de.wikipedia.org, but not
   * en.wikipedia.org, wiktionary.org, en.wiktionary.org, us.en.wikipedia.org,
@@@ -4468,178 -4381,6 +4468,178 @@@ $wgPasswordPolicy = 
        ],
  ];
  
 +/**
 + * Configure AuthManager
 + *
 + * All providers are constructed using ObjectFactory, see that for the general
 + * structure. The array may also contain a key "sort" used to order providers:
 + * providers are stably sorted by this value, which should be an integer
 + * (default is 0).
 + *
 + * Elements are:
 + * - preauth: Array (keys ignored) of specifications for PreAuthenticationProviders
 + * - primaryauth: Array (keys ignored) of specifications for PrimaryAuthenticationProviders
 + * - secondaryauth: Array (keys ignored) of specifications for SecondaryAuthenticationProviders
 + *
 + * @since 1.27
 + * @note If this is null or empty, the value from $wgAuthManagerAutoConfig is
 + *  used instead. Local customization should generally set this variable from
 + *  scratch to the desired configuration. Extensions that want to
 + *  auto-configure themselves should use $wgAuthManagerAutoConfig instead.
 + */
 +$wgAuthManagerConfig = null;
 +
 +/**
 + * @see $wgAuthManagerConfig
 + * @since 1.27
 + */
 +$wgAuthManagerAutoConfig = [
 +      'preauth' => [
 +              MediaWiki\Auth\LegacyHookPreAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\LegacyHookPreAuthenticationProvider::class,
 +                      'sort' => 0,
 +              ],
 +              MediaWiki\Auth\ThrottlePreAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\ThrottlePreAuthenticationProvider::class,
 +                      'sort' => 0,
 +              ],
 +      ],
 +      'primaryauth' => [
 +              // TemporaryPasswordPrimaryAuthenticationProvider should come before
 +              // any other PasswordAuthenticationRequest-based
 +              // PrimaryAuthenticationProvider (or at least any that might return
 +              // FAIL rather than ABSTAIN for a wrong password), or password reset
 +              // won't work right. Do not remove this (or change the key) or
 +              // auto-configuration of other such providers in extensions will
 +              // probably auto-insert themselves in the wrong place.
 +              MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class,
 +                      'args' => [ [
 +                              // Fall through to LocalPasswordPrimaryAuthenticationProvider
 +                              'authoritative' => false,
 +                      ] ],
 +                      'sort' => 0,
 +              ],
 +              MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class,
 +                      'args' => [ [
 +                              // Last one should be authoritative, or else the user will get
 +                              // a less-than-helpful error message (something like "supplied
 +                              // authentication info not supported" rather than "wrong
 +                              // password") if it too fails.
 +                              'authoritative' => true,
 +                      ] ],
 +                      'sort' => 100,
 +              ],
 +      ],
 +      'secondaryauth' => [
 +              MediaWiki\Auth\CheckBlocksSecondaryAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\CheckBlocksSecondaryAuthenticationProvider::class,
 +                      'sort' => 0,
 +              ],
 +              MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider::class,
 +                      'sort' => 100,
 +              ],
 +              // Linking during login is experimental, enable at your own risk - T134952
 +              // MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider::class => [
 +              //      'class' => MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider::class,
 +              //      'sort' => 100,
 +              // ],
 +              MediaWiki\Auth\EmailNotificationSecondaryAuthenticationProvider::class => [
 +                      'class' => MediaWiki\Auth\EmailNotificationSecondaryAuthenticationProvider::class,
 +                      'sort' => 200,
 +              ],
 +      ],
 +];
 +
 +/**
 + * Time frame for re-authentication.
 + *
 + * With only password-based authentication, you'd just ask the user to re-enter
 + * their password to verify certain operations like changing the password or
 + * changing the account's email address. But under AuthManager, the user might
 + * not have a password (you might even have to redirect the browser to a
 + * third-party service or something complex like that), you might want to have
 + * both factors of a two-factor authentication, and so on. So, the options are:
 + * - Incorporate the whole multi-step authentication flow within everything
 + *   that needs to do this.
 + * - Consider it good if they used Special:UserLogin during this session within
 + *   the last X seconds.
 + * - Come up with a third option.
 + *
 + * MediaWiki currently takes the second option. This setting configures the
 + * "X seconds".
 + *
 + * This allows for configuring different time frames for different
 + * "operations". The operations used in MediaWiki core include:
 + * - LinkAccounts
 + * - UnlinkAccount
 + * - ChangeCredentials
 + * - RemoveCredentials
 + * - ChangeEmail
 + *
 + * Additional operations may be used by extensions, either explicitly by
 + * calling AuthManager::securitySensitiveOperationStatus(),
 + * ApiAuthManagerHelper::securitySensitiveOperation() or
 + * SpecialPage::checkLoginSecurityLevel(), or implicitly by overriding
 + * SpecialPage::getLoginSecurityLevel() or by subclassing
 + * AuthManagerSpecialPage.
 + *
 + * The key 'default' is used if a requested operation isn't defined in the array.
 + *
 + * @since 1.27
 + * @var int[] operation => time in seconds. A 'default' key must always be provided.
 + */
 +$wgReauthenticateTime = [
 +      'default' => 300,
 +];
 +
 +/**
 + * Whether to allow security-sensitive operations when re-authentication is not possible.
 + *
 + * If AuthManager::canAuthenticateNow() is false (e.g. the current
 + * SessionProvider is not able to change users, such as when OAuth is in use),
 + * AuthManager::securitySensitiveOperationStatus() cannot sensibly return
 + * SEC_REAUTH. Setting an operation true here will have it return SEC_OK in
 + * that case, while setting it false will have it return SEC_FAIL.
 + *
 + * The key 'default' is used if a requested operation isn't defined in the array.
 + *
 + * @since 1.27
 + * @see $wgReauthenticateTime
 + * @var bool[] operation => boolean. A 'default' key must always be provided.
 + */
 +$wgAllowSecuritySensitiveOperationIfCannotReauthenticate = [
 +      'default' => true,
 +];
 +
 +/**
 + * List of AuthenticationRequest class names which are not changeable through
 + * Special:ChangeCredentials and the changeauthenticationdata API.
 + * This is only enforced on the client level; AuthManager itself (e.g.
 + * AuthManager::allowsAuthenticationDataChange calls) is not affected.
 + * Class names are checked for exact match (not for subclasses).
 + * @since 1.27
 + * @var string[]
 + */
 +$wgChangeCredentialsBlacklist = [
 +      \MediaWiki\Auth\TemporaryPasswordAuthenticationRequest::class
 +];
 +
 +/**
 + * List of AuthenticationRequest class names which are not removable through
 + * Special:RemoveCredentials and the removeauthenticationdata API.
 + * This is only enforced on the client level; AuthManager itself (e.g.
 + * AuthManager::allowsAuthenticationDataChange calls) is not affected.
 + * Class names are checked for exact match (not for subclasses).
 + * @since 1.27
 + * @var string[]
 + */
 +$wgRemoveCredentialsBlacklist = [
 +      \MediaWiki\Auth\PasswordAuthenticationRequest::class,
 +];
 +
  /**
   * For compatibility with old installations set to false
   * @deprecated since 1.24 will be removed in future
@@@ -4687,14 -4428,14 +4687,14 @@@ $wgPasswordDefault = 'pbkdf2'
   *
   * An advanced example:
   * @code
 - * $wgPasswordConfig['bcrypt-peppered'] = array(
 + * $wgPasswordConfig['bcrypt-peppered'] = [
   *     'class' => 'EncryptedPassword',
   *     'underlying' => 'bcrypt',
 - *     'secrets' => array(),
 + *     'secrets' => [],
   *     'cipher' => MCRYPT_RIJNDAEL_256,
   *     'mode' => MCRYPT_MODE_CBC,
   *     'cost' => 5,
 - * );
 + * ];
   * @endcode
   *
   * @since 1.24
@@@ -4726,9 -4467,9 +4726,9 @@@ $wgPasswordConfig = 
        ],
        'pbkdf2' => [
                'class' => 'Pbkdf2Password',
 -              'algo' => 'sha256',
 -              'cost' => '10000',
 -              'length' => '128',
 +              'algo' => 'sha512',
 +              'cost' => '30000',
 +              'length' => '64',
        ],
  ];
  
@@@ -4821,7 -4562,6 +4821,7 @@@ $wgDefaultUserOptions = 
        'watchcreations' => 1,
        'watchdefault' => 1,
        'watchdeletion' => 0,
 +      'watchuploads' => 1,
        'watchlistdays' => 3.0,
        'watchlisthideanons' => 0,
        'watchlisthidebots' => 0,
@@@ -4900,7 -4640,7 +4900,7 @@@ $wgSessionProviders = 
        MediaWiki\Session\BotPasswordSessionProvider::class => [
                'class' => MediaWiki\Session\BotPasswordSessionProvider::class,
                'args' => [ [
 -                      'priority' => 40,
 +                      'priority' => 75,
                ] ],
        ],
  ];
@@@ -4983,7 -4723,7 +4983,7 @@@ $wgWhitelistRead = false
   * @par Example:
   * To whitelist [[Main Page]]:
   * @code
 - * $wgWhitelistReadRegexp = array( "/Main Page/" );
 + * $wgWhitelistReadRegexp = [ "/Main Page/" ];
   * @endcode
   *
   * @note Unless ^ and/or $ is specified, a regular expression might match
   * @par Example:
   * To allow reading any page starting with 'User' regardless of the case:
   * @code
 - * $wgWhitelistReadRegexp = array( "@^UsEr.*@i" );
 + * $wgWhitelistReadRegexp = [ "@^UsEr.*@i" ];
   * @endcode
   * Will allow both [[User is banned]] and [[User:JohnDoe]]
   *
@@@ -5028,7 -4768,7 +5028,7 @@@ $wgHideIdentifiableRedirects = true
   * combined with the permissions of all groups that a given user is listed
   * in in the user_groups table.
   *
 - * Note: Don't set $wgGroupPermissions = array(); unless you know what you're
 + * Note: Don't set $wgGroupPermissions = []; unless you know what you're
   * doing! This will wipe all permissions, and may mean that your users are
   * unable to perform certain essential tasks or access new functionality
   * when new permissions are introduced and default grants established.
@@@ -5072,7 -4812,7 +5072,7 @@@ $wgGroupPermissions['user']['upload'] 
  $wgGroupPermissions['user']['reupload'] = true;
  $wgGroupPermissions['user']['reupload-shared'] = true;
  $wgGroupPermissions['user']['minoredit'] = true;
 -$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok"
 +$wgGroupPermissions['user']['purge'] = true;
  $wgGroupPermissions['user']['sendemail'] = true;
  $wgGroupPermissions['user']['applychangetags'] = true;
  $wgGroupPermissions['user']['changetags'] = true;
@@@ -5137,7 -4877,6 +5137,7 @@@ $wgGroupPermissions['sysop']['suppressr
  # $wgGroupPermissions['sysop']['upload_by_url'] = true;
  $wgGroupPermissions['sysop']['mergehistory'] = true;
  $wgGroupPermissions['sysop']['managechangetags'] = true;
 +$wgGroupPermissions['sysop']['deletechangetags'] = true;
  
  // Permission to change users' group assignments
  $wgGroupPermissions['bureaucrat']['userrights'] = true;
@@@ -5194,13 -4933,13 +5194,13 @@@ $wgImplicitGroups = [ '*', 'user', 'aut
   * @par Example:
   * To allow sysops to add themselves to the "bot" group:
   * @code
 - *    $wgGroupsAddToSelf = array( 'sysop' => array( 'bot' ) );
 + *    $wgGroupsAddToSelf = [ 'sysop' => [ 'bot' ] ];
   * @endcode
   *
   * @par Example:
   * Implicit groups may be used for the source group, for instance:
   * @code
 - *    $wgGroupsRemoveFromSelf = array( '*' => true );
 + *    $wgGroupsRemoveFromSelf = [ '*' => true ];
   * @endcode
   * This allows users in the '*' group (i.e. any user) to remove themselves from
   * any group that they happen to be in.
@@@ -5318,18 -5057,18 +5318,18 @@@ $wgAutoConfirmCount = 0
   * @todo Redocument $wgAutopromote
   *
   * The format is
 - *   array( '&' or '|' or '^' or '!', cond1, cond2, ... )
 + *   [ '&' or '|' or '^' or '!', cond1, cond2, ... ]
   * where cond1, cond2, ... are themselves conditions; *OR*
   *   APCOND_EMAILCONFIRMED, *OR*
 - *   array( APCOND_EMAILCONFIRMED ), *OR*
 - *   array( APCOND_EDITCOUNT, number of edits ), *OR*
 - *   array( APCOND_AGE, seconds since registration ), *OR*
 - *   array( APCOND_INGROUPS, group1, group2, ... ), *OR*
 - *   array( APCOND_ISIP, ip ), *OR*
 - *   array( APCOND_IPINRANGE, range ), *OR*
 - *   array( APCOND_AGE_FROM_EDIT, seconds since first edit ), *OR*
 - *   array( APCOND_BLOCKED ), *OR*
 - *   array( APCOND_ISBOT ), *OR*
 + *   [ APCOND_EMAILCONFIRMED ], *OR*
 + *   [ APCOND_EDITCOUNT, number of edits ], *OR*
 + *   [ APCOND_AGE, seconds since registration ], *OR*
 + *   [ APCOND_INGROUPS, group1, group2, ... ], *OR*
 + *   [ APCOND_ISIP, ip ], *OR*
 + *   [ APCOND_IPINRANGE, range ], *OR*
 + *   [ APCOND_AGE_FROM_EDIT, seconds since first edit ], *OR*
 + *   [ APCOND_BLOCKED ], *OR*
 + *   [ APCOND_ISBOT ], *OR*
   *   similar constructs defined by extensions.
   *
   * If $wgEmailAuthentication is off, APCOND_EMAILCONFIRMED will be true for any
@@@ -5350,7 -5089,7 +5350,7 @@@ $wgAutopromote = 
   *
   * The format is:
   * @code
 - *    array( event => criteria, ... )
 + *    [ event => criteria, ... ]
   * @endcode
   * Where event is either:
   *    - 'onEdit' (when user edits)
@@@ -5381,15 -5120,15 +5381,15 @@@ $wgAutopromoteOnceLogInRC = true
   * @endcode
   * Bureaucrats can only remove bots and sysops:
   * @code
 - * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
 + * $wgRemoveGroups['bureaucrat'] = [ 'bot', 'sysop' ];
   * @endcode
   * Sysops can make bots:
   * @code
 - * $wgAddGroups['sysop'] = array( 'bot' );
 + * $wgAddGroups['sysop'] = [ 'bot' ];
   * @endcode
   * Sysops can disable other sysops in an emergency, and disable bots:
   * @code
 - * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
 + * $wgRemoveGroups['sysop'] = [ 'sysop', 'bot' ];
   * @endcode
   */
  $wgAddGroups = [];
@@@ -5460,15 -5199,15 +5460,15 @@@ $wgEnableDnsBlacklist = false
   *
   * @par Example:
   * @code
 - * $wgDnsBlacklistUrls = array(
 + * $wgDnsBlacklistUrls = [
   *   // String containing URL
   *   'http.dnsbl.sorbs.net.',
   *   // Array with URL and key, for services that require a key
 - *   array( 'dnsbl.httpbl.net.', 'mykey' ),
 + *   [ 'dnsbl.httpbl.net.', 'mykey' ],
   *   // Array with just the URL. While this works, it is recommended that you
   *   // just use a string as shown above
 - *   array( 'opm.tornevall.org.' )
 - * );
 + *   [ 'opm.tornevall.org.' ]
 + * ];
   * @endcode
   *
   * @note You should end the domain name with a . to avoid searching your
@@@ -5500,21 -5239,21 +5500,21 @@@ $wgApplyIpBlocksToXff = false
   * @par Example:
   * To set a generic maximum of 4 hits in 60 seconds:
   * @code
 - *     $wgRateLimits = array( 4, 60 );
 + *     $wgRateLimits = [ 4, 60 ];
   * @endcode
   *
   * @par Example:
   * You could also limit per action and then type of users.
   * @code
 - *     $wgRateLimits = array(
 - *         'edit' => array(
 - *             'anon' => array( x, y ), // any and all anonymous edits (aggregate)
 - *             'user' => array( x, y ), // each logged-in user
 - *             'newbie' => array( x, y ), // each new autoconfirmed accounts; overrides 'user'
 - *             'ip' => array( x, y ), // each anon and recent account
 - *             'subnet' => array( x, y ), // ... within a /24 subnet in IPv4 or /64 in IPv6
 - *         )
 - *     )
 + *     $wgRateLimits = [
 + *         'edit' => [
 + *             'anon' => [ x, y ], // any and all anonymous edits (aggregate)
 + *             'user' => [ x, y ], // each logged-in user
 + *             'newbie' => [ x, y ], // each new autoconfirmed accounts; overrides 'user'
 + *             'ip' => [ x, y ], // each anon and recent account
 + *             'subnet' => [ x, y ], // ... within a /24 subnet in IPv4 or /64 in IPv6
 + *         ]
 + *     ]
   * @endcode
   *
   * @warning Requires that $wgMainCacheType is set to something persistent
@@@ -5639,6 -5378,7 +5639,6 @@@ $wgGrantPermissions = []
  
  $wgGrantPermissions['basic']['autoconfirmed'] = true;
  $wgGrantPermissions['basic']['autopatrol'] = true;
 -$wgGrantPermissions['basic']['autoreview'] = true;
  $wgGrantPermissions['basic']['editsemiprotected'] = true;
  $wgGrantPermissions['basic']['ipblock-exempt'] = true;
  $wgGrantPermissions['basic']['nominornewtalk'] = true;
@@@ -5646,6 -5386,7 +5646,6 @@@ $wgGrantPermissions['basic']['patrolmar
  $wgGrantPermissions['basic']['purge'] = true;
  $wgGrantPermissions['basic']['read'] = true;
  $wgGrantPermissions['basic']['skipcaptcha'] = true;
 -$wgGrantPermissions['basic']['torunblocked'] = true;
  $wgGrantPermissions['basic']['writeapi'] = true;
  
  $wgGrantPermissions['highvolume']['bot'] = true;
@@@ -5720,8 -5461,6 +5720,8 @@@ $wgGrantPermissions['sendemail']['sende
  
  $wgGrantPermissions['createaccount']['createaccount'] = true;
  
 +$wgGrantPermissions['privateinfo']['viewmyprivateinfo'] = true;
 +
  /**
   * @var Array Map of grants to their UI grouping
   * @since 1.27
@@@ -5755,8 -5494,6 +5755,8 @@@ $wgGrantPermissionGroups = 
        'createaccount'       => 'administration',
  
        'highvolume'          => 'high-volume',
 +
 +      'privateinfo'         => 'private-information',
  ];
  
  /**
@@@ -5817,6 -5554,14 +5817,6 @@@ $wgProxyList = []
   */
  $wgCookieExpiration = 180 * 86400;
  
 -/**
 - * The identifiers of the login cookies that can have their lifetimes
 - * extended independently of all other login cookies.
 - *
 - * @var string[]
 - */
 -$wgExtendedLoginCookies = [ 'UserID', 'Token' ];
 -
  /**
   * Default login cookie lifetime, in seconds. Setting
   * $wgExtendLoginCookieExpiration to null will use $wgCookieExpiration to
@@@ -5973,12 -5718,6 +5973,12 @@@ $wgTrxProfilerLimits = 
                'writes' => 0,
                'readQueryTime' => 5
        ],
 +      // Deferred updates that run after HTTP response is sent
 +      'PostSend' => [
 +              'readQueryTime' => 5,
 +              'writeQueryTime' => 1,
 +              'maxAffected' => 500
 +      ],
        // Background job runner
        'JobRunner' => [
                'readQueryTime' => 30,
   *
   * @par Advanced example:
   * @code
 - * $wgDebugLogGroups['memcached'] = array(
 + * $wgDebugLogGroups['memcached'] = [
   *     'destination' => '/var/log/mediawiki/memcached.log',
   *     'sample' => 1000,  // log 1 message out of every 1,000.
   *     'level' => \Psr\Log\LogLevel::WARNING
 - * );
 + * ];
   * @endcode
   */
  $wgDebugLogGroups = [];
   *
   * @par To completely disable logging:
   * @code
 - * $wgMWLoggerDefaultSpi = array( 'class' => '\\MediaWiki\\Logger\\NullSpi' );
 + * $wgMWLoggerDefaultSpi = [ 'class' => '\\MediaWiki\\Logger\\NullSpi' ];
   * @endcode
   *
   * @since 1.25
@@@ -6156,12 -5895,20 +6156,20 @@@ $wgStatsdServer = false
  /**
   * Prefix for metric names sent to $wgStatsdServer.
   *
 - * @see RequestContext::getStats
 + * @see MediaWikiServices::getStatsdDataFactory
   * @see BufferingStatsdDataFactory
   * @since 1.25
   */
  $wgStatsdMetricPrefix = 'MediaWiki';
  
+ /**
+  * Sampling rate for statsd metrics as an associative array of patterns and rates.
+  * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+  * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+  * @since 1.28
+  */
+ $wgStatsdSamplingRates = [];
  /**
   * InfoAction retrieves a list of transclusion links (both to and from).
   * This number puts a limit on that query in the case of highly transcluded
@@@ -6348,10 -6095,10 +6356,10 @@@ $wgSitemapNamespaces = false
   * This should be a map of namespace IDs to priority
   * @par Example:
   * @code
 - *  $wgSitemapNamespacesPriorities = array(
 + *  $wgSitemapNamespacesPriorities = [
   *      NS_USER => '0.9',
   *      NS_HELP => '0.0',
 - *  );
 + *  ];
   * @endcode
   */
  $wgSitemapNamespacesPriorities = false;
@@@ -6558,18 -6305,18 +6566,18 @@@ $wgRCLinkDays = [ 1, 3, 7, 14, 30 ]
   *  The JSON-specific options are:
   *   * 'channel' -- if set, the 'channel' parameter is also set in JSON values.
   *
 - * @example $wgRCFeeds['example'] = array(
 + * @example $wgRCFeeds['example'] = [
   *            'formatter' => 'JSONRCFeedFormatter',
   *            'uri' => "udp://localhost:1336",
   *            'add_interwiki_prefix' => false,
   *            'omit_bots' => true,
 - *    );
 - * @example $wgRCFeeds['exampleirc'] = array(
 + *    ];
 + * @example $wgRCFeeds['exampleirc'] = [
   *            'formatter' => 'IRCColourfulRCFeedFormatter',
   *            'uri' => "udp://localhost:1338",
   *            'add_interwiki_prefix' => false,
   *            'omit_bots' => true,
 - *    );
 + *    ];
   * @since 1.22
   */
  $wgRCFeeds = [];
@@@ -6731,7 -6478,7 +6739,7 @@@ $wgUnwatchedPageThreshold = false
   *
   * To register a new one:
   * @code
 - * $wgRecentChangesFlags['flag'] => array(
 + * $wgRecentChangesFlags['flag'] => [
   *   // message for the letter displayed next to rows on changes lists
   *   'letter' => 'letter-msg',
   *   // message for the tooltip of the letter
   *   // will set the top-level flag if any line contains the flag, 'all' will
   *   // only be set if all lines contain the flag.
   *   'grouping' => 'any',
 - * );
 + * ];
   * @endcode
   *
   * @since 1.22
@@@ -6850,11 -6597,11 +6858,11 @@@ $wgShowCreditsIfMax = true
   * subprojects on the interwiki map of the target wiki, or a mix of the two,
   * e.g.
   * @code
 - *     $wgImportSources = array(
 - *         'wikipedia' => array( 'cs', 'en', 'fr', 'zh' ),
 + *     $wgImportSources = [
 + *         'wikipedia' => [ 'cs', 'en', 'fr', 'zh' ],
   *         'wikispecies',
 - *         'wikia' => array( 'animanga', 'brickipedia', 'desserts' ),
 - *     );
 + *         'wikia' => [ 'animanga', 'brickipedia', 'desserts' ],
 + *     ];
   * @endcode
   *
   * If you have a very complex import sources setup, you can lazy-load it using
@@@ -6982,11 -6729,11 +6990,11 @@@ $wgExtensionMessagesFiles = []
   *
   * @par Complex example:
   * @code
 - *    $wgMessagesDirs['Example'] = array(
 + *    $wgMessagesDirs['Example'] = [
   *        __DIR__ . '/lib/ve/i18n',
   *        __DIR__ . '/lib/oojs-ui/i18n',
   *        __DIR__ . '/i18n',
 - *    )
 + *    ]
   * @endcode
   * @since 1.23
   */
@@@ -7056,18 -6803,18 +7064,18 @@@ $wgAutoloadAttemptLowercase = true
   * All but 'name', 'path' and 'author' can be omitted.
   *
   * @code
 - * $wgExtensionCredits[$type][] = array(
 + * $wgExtensionCredits[$type][] = [
   *     'path' => __FILE__,
   *     'name' => 'Example extension',
   *     'namemsg' => 'exampleextension-name',
 - *     'author' => array(
 + *     'author' => [
   *         'Foo Barstein',
 - *     ),
 + *     ],
   *     'version' => '1.9.0',
   *     'url' => 'http://example.org/example-extension/',
   *     'descriptionmsg' => 'exampleextension-desc',
   *     'license-name' => 'GPL-2.0+',
 - * );
 + * ];
   * @endcode
   *
   * The extensions are listed on Special:Version. This page also looks for a file
@@@ -7107,7 -6854,6 +7115,7 @@@ $wgExtensionCredits = []
  /**
   * Authentication plugin.
   * @var $wgAuth AuthPlugin
 + * @deprecated since 1.27 use $wgAuthManagerConfig instead
   */
  $wgAuth = null;
  
   * @endcode
   * - A function with some data:
   * @code
 - *     $wgHooks['event_name'][] = array( $function, $data );
 + *     $wgHooks['event_name'][] = [ $function, $data ];
   * @endcode
   * - A an object method:
   * @code
 - *     $wgHooks['event_name'][] = array( $object, 'method' );
 + *     $wgHooks['event_name'][] = [ $object, 'method' ];
   * @endcode
   * - A closure:
   * @code
@@@ -7255,7 -7001,7 +7263,7 @@@ $wgSpecialPageCacheUpdates = 
   * Hooks that are used for outputting exceptions.  Format is:
   *   $wgExceptionHooks[] = $funcname
   * or:
 - *   $wgExceptionHooks[] = array( $class, $funcname )
 + *   $wgExceptionHooks[] = [ $class, $funcname ]
   * Hooks should return strings or false
   */
  $wgExceptionHooks = [];
@@@ -7371,7 -7117,10 +7379,7 @@@ $wgLogRestrictions = 
   *
   * @par Example:
   * @code
 - *   $wgFilterLogTypes = array(
 - *      'move' => true,
 - *      'import' => false,
 - *   );
 + *   $wgFilterLogTypes = [ 'move' => true, 'import' => false ];
   * @endcode
   *
   * Will display show/hide links for the move and import logs. Move logs will be
@@@ -7524,7 -7273,7 +7532,7 @@@ $wgActionFilteredLogs = 
        ],
        'newusers' => [
                'create' => [ 'create', 'newusers' ],
 -              'create2' => ['create2' ],
 +              'create2' => [ 'create2' ],
                'autocreate' => [ 'autocreate' ],
                'byemail' => [ 'byemail' ],
        ],
                'protect' => [ 'protect' ],
                'modify' => [ 'modify' ],
                'unprotect' => [ 'unprotect' ],
 -              'move_prot' => ['move_prot'],
 +              'move_prot' => [ 'move_prot' ],
        ],
        'rights' => [
                'rights' => [ 'rights' ],
@@@ -7655,7 -7404,7 +7663,7 @@@ $wgDefaultRobotPolicy = 'index,follow'
   *
   * @par Example:
   * @code
 - *   $wgNamespaceRobotPolicies = array( NS_TALK => 'noindex' );
 + *   $wgNamespaceRobotPolicies = [ NS_TALK => 'noindex' ];
   * @endcode
   */
  $wgNamespaceRobotPolicies = [];
   *
   * @par Example:
   * @code
 - * $wgArticleRobotPolicies = array(
 + * $wgArticleRobotPolicies = [
   *         'Main Page' => 'noindex,follow',
   *         'User:Bob' => 'index,follow',
 - * );
 + * ];
   * @endcode
   *
   * @par Example that DOES NOT WORK because the names are not canonical text
   * forms:
   * @code
 - *   $wgArticleRobotPolicies = array(
 + *   $wgArticleRobotPolicies = [
   *     # Underscore, not space!
   *     'Main_Page' => 'noindex,follow',
   *     # "Project", not the actual project name!
   *     'Project:X' => 'index,follow',
   *     # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false for that namespace)!
   *     'abc' => 'noindex,nofollow'
 - *   );
 + *   ];
   * @endcode
   */
  $wgArticleRobotPolicies = [];
   *
   * @par Example:
   * @code
 - *   $wgExemptFromUserRobotsControl = array( NS_MAIN, NS_TALK, NS_PROJECT );
 + *   $wgExemptFromUserRobotsControl = [ NS_MAIN, NS_TALK, NS_PROJECT ];
   * @endcode
   */
  $wgExemptFromUserRobotsControl = null;
@@@ -7764,14 -7513,14 +7772,14 @@@ $wgDebugAPI = false
   *
   * @code
   *  $wgAPIModules['foo'] = 'ApiFoo';
 - *  $wgAPIModules['bar'] = array(
 + *  $wgAPIModules['bar'] = [
   *    'class' => 'ApiBar',
   *    'factory' => function( $main, $name ) { ... }
 - *  );
 - *  $wgAPIModules['xyzzy'] = array(
 + *  ];
 + *  $wgAPIModules['xyzzy'] = [
   *    'class' => 'ApiXyzzy',
 - *    'factory' => array( 'XyzzyFactory', 'newApiModule' )
 - *  );
 + *    'factory' => [ 'XyzzyFactory', 'newApiModule' ]
 + *  ];
   * @endcode
   *
   * Extension modules may override the core modules.
@@@ -7901,12 -7650,12 +7909,12 @@@ $wgAjaxEditStash = true
   *
   * @par Example:
   * @code
 - * $wgCrossSiteAJAXdomains = array(
 + * $wgCrossSiteAJAXdomains = [
   *     'www.mediawiki.org',
   *     '*.wikipedia.org',
   *     '*.wikimedia.org',
   *     '*.wiktionary.org',
 - * );
 + * ];
   * @endcode
   */
  $wgCrossSiteAJAXdomains = [];
@@@ -8043,13 -7792,9 +8051,13 @@@ $wgJobRunRate = 1
   * When $wgJobRunRate > 0, try to run jobs asynchronously, spawning a new process
   * to handle the job execution, instead of blocking the request until the job
   * execution finishes.
 + *
   * @since 1.23
   */
 -$wgRunJobsAsync = true;
 +$wgRunJobsAsync = (
 +      !function_exists( 'register_postsend_function' ) &&
 +      !function_exists( 'fastcgi_finish_request' )
 +);
  
  /**
   * Number of rows to update per job
@@@ -8070,9 -7815,10 +8078,9 @@@ $wgUpdateRowsPerQuery = 100
  
  /**
   * Name of the external diff engine to use. Supported values:
 - * * false: default PHP implementation, DairikiDiff
 - * * 'wikidiff2': Wikimedia's fast difference engine implemented as a PHP/HHVM module
 - * * 'wikidiff3': newer PHP-based difference engine
 - * * any other string is treated as a path to external diff executable
 + * * string: path to an external diff executable
 + * * false: wikidiff2 PHP/HHVM module if installed, otherwise the default PHP implementation
 + * * 'wikidiff', 'wikidiff2', and 'wikidiff3' are treated as false for backwards compatibility
   */
  $wgExternalDiffEngine = false;
  
@@@ -8126,13 -7872,13 +8134,13 @@@ $wgRedirectOnLogin = null
   *
   * @par Example:
   * @code
 - *   $wgPoolCounterConf = array( 'ArticleView' => array(
 + *   $wgPoolCounterConf = [ 'ArticleView' => [
   *     'class' => 'PoolCounter_Client',
   *     'timeout' => 15, // wait timeout in seconds
   *     'workers' => 5, // maximum number of active threads in each pool
   *     'maxqueue' => 50, // maximum number of total threads in each pool
   *     ... any extension-specific options...
 - *   );
 + *   ];
   * @endcode
   */
  $wgPoolCounterConf = null;
@@@ -8195,6 -7941,13 +8203,6 @@@ $wgTextModelsToParse = 
        CONTENT_MODEL_CSS, // Make categories etc work, people put them into comments.
  ];
  
 -/**
 - * Whether the user must enter their password to change their e-mail address
 - *
 - * @since 1.20
 - */
 -$wgRequirePasswordforEmailChange = true;
 -
  /**
   * Register handlers for specific types of sites.
   *
@@@ -8219,23 -7972,6 +8227,23 @@@ $wgPagePropsHaveSortkey = true
   */
  $wgHttpsPort = 443;
  
 +/**
 + * Secret for session storage.
 + * This should be set in LocalSettings.php, otherwise wgSecretKey will
 + * be used.
 + * @since 1.27
 + */
 +$wgSessionSecret = false;
 +
 +/**
 + * If for some reason you can't install the PHP OpenSSL or mcrypt extensions,
 + * you can set this to true to make MediaWiki work again at the cost of storing
 + * sensitive session data insecurely. But it would be much more secure to just
 + * install the OpenSSL extension.
 + * @since 1.27
 + */
 +$wgSessionInsecureSecrets = false;
 +
  /**
   * Secret for hmac-based key derivation function (fast,
   * cryptographically secure random numbers).
@@@ -8264,43 -8000,24 +8272,43 @@@ $wgPageLanguageUseDB = false
  
  /**
   * Global configuration variable for Virtual REST Services.
 - * Parameters for different services are to be declared inside
 - * $wgVirtualRestConfig['modules'], which is to be treated as an associative
 - * array. Global parameters will be merged with service-specific ones. The
 - * result will then be passed to VirtualRESTService::__construct() in the
 - * module.
 + *
 + * Use the 'path' key to define automatically mounted services. The value for this
 + * key is a map of path prefixes to service configuration. The later is an array of:
 + *   - class : the fully qualified class name
 + *   - options : map of arguments to the class constructor
 + * Such services will be available to handle queries under their path from the VRS
 + * singleton, e.g. MediaWikiServices::getInstance()->getVirtualRESTServiceClient();
 + *
 + * Auto-mounting example for Parsoid:
 + *
 + * $wgVirtualRestConfig['paths']['/parsoid/'] = [
 + *     'class' => 'ParsoidVirtualRESTService',
 + *     'options' => [
 + *         'url' => 'http://localhost:8000',
 + *         'prefix' => 'enwiki',
 + *         'domain' => 'en.wikipedia.org'
 + *     ]
 + * ];
 + *
 + * Parameters for different services can also be declared inside the 'modules' value,
 + * which is to be treated as an associative array. The parameters in 'global' will be
 + * merged with service-specific ones. The result will then be passed to
 + * VirtualRESTService::__construct() in the module.
   *
   * Example config for Parsoid:
   *
 - *   $wgVirtualRestConfig['modules']['parsoid'] = array(
 + *   $wgVirtualRestConfig['modules']['parsoid'] = [
   *     'url' => 'http://localhost:8000',
   *     'prefix' => 'enwiki',
   *     'domain' => 'en.wikipedia.org',
 - *   );
 + *   ];
   *
   * @var array
   * @since 1.25
   */
  $wgVirtualRestConfig = [
 +      'paths' => [],
        'modules' => [],
        'global' => [
                # Timeout in seconds
@@@ -8343,7 -8060,7 +8351,7 @@@ $wgPopularPasswordFile = __DIR__ . '/..
  $wgMaxUserDBWriteDuration = false;
  
  /**
 - * Mapping of event channels to EventRelayer configuration.
 + * Mapping of event channels (or channel categories) to EventRelayer configuration.
   *
   * By setting up a PubSub system (like Kafka) and enabling a corresponding EventRelayer class
   * that uses it, MediaWiki can broadcast events to all subscribers. Certain features like WAN
   * subscribe to the channel and take actions based on the events. For example, a local daemon
   * can run on each CDN cache node and perfom local purges based on the URL purge channel events.
   *
 - * The 'default' channel is for all channels without an explicit entry here.
 + * Some extensions may want to use "channel categories" so that different channels can also share
 + * the same custom relayer instance (e.g. when it's likely to be overriden). They can use
 + * EventRelayerGroup::getRelayer() based on the category but call notify() on various different
 + * actual channels. One reason for this would be that some system have very different performance
 + * vs durability needs, so one system (e.g. Kafka) may not be suitable for all uses.
 + *
 + * The 'default' key is for all channels (or channel categories) without an explicit entry here.
   *
   * @since 1.27
   */
@@@ -8367,43 -8078,6 +8375,43 @@@ $wgEventRelayerConfig = 
        ]
  ];
  
 +/**
 + * Share data about this installation with MediaWiki developers
 + *
 + * When set to true, MediaWiki will periodically ping https://www.mediawiki.org/ with basic
 + * data about this MediaWiki instance. This data includes, for example, the type of system,
 + * PHP version, and chosen database backend. The Wikimedia Foundation shares this data with
 + * MediaWiki developers to help guide future development efforts.
 + *
 + * For details about what data is sent, see: https://www.mediawiki.org/wiki/Manual:$wgPingback
 + *
 + * @var bool
 + * @since 1.28
 + */
 +$wgPingback = false;
 +
 +/**
 + * List of urls which appear often to be triggering CSP reports
 + * but do not appear to be caused by actual content, but by client
 + * software inserting scripts (i.e. Ad-Ware).
 + * List based on results from Wikimedia logs.
 + *
 + * @since 1.28
 + */
 +$wgCSPFalsePositiveUrls = [
 +      'https://3hub.co' => true,
 +      'https://morepro.info' => true,
 +      'https://p.ato.mx' => true,
 +      'https://s.ato.mx' => true,
 +      'https://adserver.adtech.de' => true,
 +      'https://ums.adtechus.com' => true,
 +      'https://cas.criteo.com' => true,
 +      'https://cat.nl.eu.criteo.com' => true,
 +      'https://atpixel.alephd.com' => true,
 +      'https://rtb.metrigo.com' => true,
 +      'https://d5p.de17a.com' => true,
 +];
 +
  /**
   * For really cool vim folding this needs to be at the end:
   * vim: foldmarker=@{,@} foldmethod=marker
@@@ -30,6 -30,7 +30,6 @@@ use MediaWiki\Session\SessionManager
  
  // Hide compatibility functions from Doxygen
  /// @cond
 -
  /**
   * Compatibility functions
   *
@@@ -222,17 -223,17 +222,17 @@@ function wfAppendToArrayIfNotDefault( $
   * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal
   * e.g.
   *    wfMergeErrorArrays(
 - *            array( array( 'x' ) ),
 - *            array( array( 'x', '2' ) ),
 - *            array( array( 'x' ) ),
 - *            array( array( 'y' ) )
 + *            [ [ 'x' ] ],
 + *            [ [ 'x', '2' ] ],
 + *            [ [ 'x' ] ],
 + *            [ [ 'y' ] ]
   *    );
   * returns:
 - *            array(
 - *            array( 'x', '2' ),
 - *            array( 'x' ),
 - *            array( 'y' )
 - *    )
 + *            [
 + *            [ 'x', '2' ],
 + *            [ 'x' ],
 + *            [ 'y' ]
 + *    ]
   *
   * @param array $array1,...
   * @return array
@@@ -500,26 -501,12 +500,26 @@@ function wfAppendQuery( $url, $query ) 
                $query = wfArrayToCgi( $query );
        }
        if ( $query != '' ) {
 +              // Remove the fragment, if there is one
 +              $fragment = false;
 +              $hashPos = strpos( $url, '#' );
 +              if ( $hashPos !== false ) {
 +                      $fragment = substr( $url, $hashPos );
 +                      $url = substr( $url, 0, $hashPos );
 +              }
 +
 +              // Add parameter
                if ( false === strpos( $url, '?' ) ) {
                        $url .= '?';
                } else {
                        $url .= '&';
                }
                $url .= $query;
 +
 +              // Put the fragment back
 +              if ( $fragment !== false ) {
 +                      $url .= $fragment;
 +              }
        }
        return $url;
  }
@@@ -811,7 -798,7 +811,7 @@@ function wfUrlProtocolsWithoutProtRel(
   * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)).
   *
   * @param string $url A URL to parse
 - * @return string[] Bits of the URL in an associative array, per PHP docs
 + * @return string[]|bool Bits of the URL in an associative array, per PHP docs, false on failure
   */
  function wfParseUrl( $url ) {
        global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
        $bits = parse_url( $url );
        MediaWiki\restoreWarnings();
        // parse_url() returns an array without scheme for some invalid URLs, e.g.
 -      // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
 +      // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
        if ( !$bits || !isset( $bits['scheme'] ) ) {
                return false;
        }
@@@ -1195,6 -1182,7 +1195,7 @@@ function wfLogProfilingData() 
                        $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
                        $statsdSender = new SocketSender( $statsdHost, $statsdPort );
                        $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
+                       $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
                        $statsdClient->send( $context->getStats()->getBuffer() );
                } catch ( Exception $ex ) {
                        MWExceptionHandler::logException( $ex );
@@@ -2133,24 -2121,6 +2134,24 @@@ function wfTempDir() 
                        return $tmp;
                }
        }
 +
 +      /**
 +       * PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to it
 +       * so create a directory within that called 'mwtmp' with a suffix of the user running the
 +       * current process.
 +       * The user is included as if various scripts are run by different users they will likely
 +       * not be able to access each others temporary files.
 +       */
 +      if ( wfIsWindows() ) {
 +              $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp' . '-' . get_current_user();
 +              if ( !file_exists( $tmp ) ) {
 +                      mkdir( $tmp );
 +              }
 +              if ( file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
 +                      return $tmp;
 +              }
 +      }
 +
        throw new MWException( 'No writable temporary directory could be found. ' .
                'Please set $wgTmpDirectory to a writable directory.' );
  }
@@@ -2456,15 -2426,6 +2457,15 @@@ function wfShellExec( $cmd, &$retval = 
        }
        wfDebug( "wfShellExec: $cmd\n" );
  
 +      // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN.
 +      // Other platforms may be more accomodating, but we don't want to be
 +      // accomodating, because very long commands probably include user
 +      // input. See T129506.
 +      if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
 +              throw new Exception( __METHOD__ .
 +                      '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
 +      }
 +
        $desc = [
                0 => [ 'file', 'php://stdin', 'r' ],
                1 => [ 'pipe', 'w' ],
        $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
        $eintrMessage = "stream_select(): unable to select [$eintr]";
  
 -      // Build a table mapping resource IDs to pipe FDs to work around a
 -      // PHP 5.3 issue in which stream_select() does not preserve array keys
 -      // <https://bugs.php.net/bug.php?id=53427>.
 -      $fds = [];
 -      foreach ( $pipes as $fd => $pipe ) {
 -              $fds[(int)$pipe] = $fd;
 -      }
 -
        $running = true;
        $timeout = null;
        $numReadyPipes = 0;
                                break;
                        }
                }
 -              foreach ( $readyPipes as $pipe ) {
 +              foreach ( $readyPipes as $fd => $pipe ) {
                        $block = fread( $pipe, 65536 );
 -                      $fd = $fds[(int)$pipe];
                        if ( $block === '' ) {
                                // End of file
                                fclose( $pipes[$fd] );
@@@ -2950,7 -2920,7 +2951,7 @@@ function wfRelativePath( $path, $from 
   * Supports base 2 through 36; digit values 10-36 are represented
   * as lowercase letters a-z. Input is case-insensitive.
   *
 - * @deprecated 1.27 Use Wikimedia\base_convert() directly
 + * @deprecated since 1.27 Use Wikimedia\base_convert() directly
   *
   * @param string $input Input number
   * @param int $sourceBase Base of the input number
@@@ -3350,9 -3320,9 +3351,9 @@@ function wfCountDown( $seconds ) 
  }
  
  /**
 - * Replace all invalid characters with -
 - * Additional characters can be defined in $wgIllegalFileChars (see bug 20489)
 - * By default, $wgIllegalFileChars = ':'
 + * Replace all invalid characters with '-'.
 + * Additional characters can be defined in $wgIllegalFileChars (see T22489).
 + * By default, $wgIllegalFileChars includes ':', '/', '\'.
   *
   * @param string $name Filename to process
   * @return string
  function wfStripIllegalFilenameChars( $name ) {
        global $wgIllegalFileChars;
        $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
 -      $name = wfBaseName( $name );
        $name = preg_replace(
                "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
                '-',
                $name
        );
 +      // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
 +      $name = wfBaseName( $name );
        return $name;
  }
  
@@@ -3525,7 -3494,7 +3526,7 @@@ function wfGetParserCacheStorage() 
   * @param string|null $deprecatedVersion Optionally mark hook as deprecated with version number
   *
   * @return bool True if no handler aborted the hook
 - * @deprecated 1.25 - use Hooks::run
 + * @deprecated since 1.25 - use Hooks::run
   */
  function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
        return Hooks::run( $event, $args, $deprecatedVersion );