resourceloader: Remove comment about XmlJsCode wrapper
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoader.php
index b05068c..4a23eae 100644 (file)
@@ -36,65 +36,41 @@ use Wikimedia\WrappedString;
  *    https://www.mediawiki.org/wiki/ResourceLoader
  */
 class ResourceLoader implements LoggerAwareInterface {
-       /** @var int */
-       const CACHE_VERSION = 8;
+       /** @var Config $config */
+       protected $config;
+       /** @var MessageBlobStore */
+       protected $blobStore;
 
-       /** @var bool */
-       protected static $debugMode = null;
+       /** @var LoggerInterface */
+       private $logger;
 
-       /**
-        * Module name/ResourceLoaderModule object pairs
-        * @var array
-        */
+       /** @var ResourceLoaderModule[] Map of (module name => ResourceLoaderModule) */
        protected $modules = [];
-
-       /**
-        * Associative array mapping module name to info associative array
-        * @var array
-        */
+       /** @var array[] Map of (module name => associative info array) */
        protected $moduleInfos = [];
-
-       /** @var Config $config */
-       protected $config;
-
        /**
-        * List of module names that contain QUnit test suites
-        * @var string[]
+        * Associative array mapping framework ids to a list of names of test suite modules
+        * like [ 'qunit' => [ 'mediawiki.tests.qunit.suites', 'ext.foo.tests', ... ], ... ]
+        * @var array
         */
+       protected $testModuleNames = [];
+       /** @var string[] List of module names that contain QUnit test suites */
        protected $testSuiteModuleNames = [];
 
-       /**
-        * E.g. [ 'source-id' => 'http://.../load.php' ]
-        * @var array
-        */
+       /** @var array Map of (source => path); E.g. [ 'source-id' => 'http://.../load.php' ] */
        protected $sources = [];
-
-       /**
-        * Errors accumulated during current respond() call.
-        * @var array
-        */
+       /** @var array Errors accumulated during current respond() call */
        protected $errors = [];
-
-       /**
-        * List of extra HTTP response headers provided by loaded modules.
-        *
-        * Populated by makeModuleResponse().
-        *
-        * @var array
-        */
+       /** @var string[] Extra HTTP response headers from modules loaded in makeModuleResponse() */
        protected $extraHeaders = [];
 
-       /**
-        * @var MessageBlobStore
-        */
-       protected $blobStore;
+       /** @var bool */
+       protected static $debugMode = null;
 
-       /**
-        * @var LoggerInterface
-        */
-       private $logger;
+       /** @var int */
+       const CACHE_VERSION = 8;
 
-       /** @var string JavaScript / CSS pragma to disable minification. **/
+       /** @var string JavaScript / CSS pragma to disable minification. * */
        const FILTER_NOMIN = '/*@nomin*/';
 
        /**
@@ -571,13 +547,81 @@ class ResourceLoader implements LoggerAwareInterface {
        }
 
        /**
+        * @internal For use by ResourceLoaderStartUpModule only.
+        */
+       const HASH_LENGTH = 5;
+
+       /**
+        * Create a hash for module versioning purposes.
+        *
+        * This hash is used in three ways:
+        *
+        * - To differentiate between the current version and a past version
+        *   of a module by the same name.
+        *
+        *   In the cache key of localStorage in the browser (mw.loader.store).
+        *   This store keeps only one version of any given module. As long as the
+        *   next version the client encounters has a different hash from the last
+        *   version it saw, it will correctly discard it in favour of a network fetch.
+        *
+        *   A browser may evict a site's storage container for any reason (e.g. when
+        *   the user hasn't visited a site for some time, and/or when the device is
+        *   low on storage space). Anecdotally it seems devices rarely keep unused
+        *   storage beyond 2 weeks on mobile devices and 4 weeks on desktop.
+        *   But, there is no hard limit or expiration on localStorage.
+        *   ResourceLoader's Client also clears localStorage when the user changes
+        *   their language preference or when they (temporarily) use Debug Mode.
+        *
+        *   The only hard factors that reduce the range of possible versions are
+        *   1) the name and existence of a given module, and
+        *   2) the TTL for mw.loader.store, and
+        *   3) the `$wgResourceLoaderStorageVersion` configuration variable.
+        *
+        * - To identify a batch response of modules from load.php in an HTTP cache.
+        *
+        *   When fetching modules in a batch from load.php, a combined hash
+        *   is created by the JS code, and appended as query parameter.
+        *
+        *   In cache proxies (e.g. Varnish, Nginx) and in the browser's HTTP cache,
+        *   these urls are used to identify other previously cached responses.
+        *   The range of possible versions a given version has to be unique amongst
+        *   is determined by the maximum duration each response is stored for, which
+        *   is controlled by `$wgResourceLoaderMaxage['versioned']`.
+        *
+        * - To detect race conditions between multiple web servers in a MediaWiki
+        *   deployment of which some have the newer version and some still the older
+        *   version.
+        *
+        *   An HTTP request from a browser for the Startup manifest may be responded
+        *   to by a server with the newer version. The browser may then use that to
+        *   request a given module, which may then be responded to by a server with
+        *   the older version. To avoid caching this for too long (which would pollute
+        *   all other users without repairing itself), the combined hash that the JS
+        *   client adds to the url is verified by the server (in ::sendResponseHeaders).
+        *   If they don't match, we instruct cache proxies and clients to not cache
+        *   this response as long as they normally would. This is also the reason
+        *   that the algorithm used here in PHP must match the one used in JS.
+        *
+        * The fnv132 digest creates a 32-bit integer, which goes upto 4 Giga and
+        * needs up to 7 chars in base 36.
+        * Within 7 characters, base 36 can count up to 78,364,164,096 (78 Giga),
+        * (but with fnv132 we'd use very little of this range, mostly padding).
+        * Within 6 characters, base 36 can count up to 2,176,782,336 (2 Giga).
+        * Within 5 characters, base 36 can count up to 60,466,176 (60 Mega).
+        *
         * @since 1.26
         * @param string $value
         * @return string Hash
         */
        public static function makeHash( $value ) {
                $hash = hash( 'fnv132', $value );
-               return Wikimedia\base_convert( $hash, 16, 36, 7 );
+               // The base_convert will pad it (if too short),
+               // then substr() will trim it (if too long).
+               return substr(
+                       Wikimedia\base_convert( $hash, 16, 36, self::HASH_LENGTH ),
+                       0,
+                       self::HASH_LENGTH
+               );
        }
 
        /**
@@ -685,8 +729,9 @@ class ResourceLoader implements LoggerAwareInterface {
                                // Do not allow private modules to be loaded from the web.
                                // This is a security issue, see T36907.
                                if ( $module->getGroup() === 'private' ) {
+                                       // Not a serious error, just means something is trying to access it (T101806)
                                        $this->logger->debug( "Request for private module '$name' denied" );
-                                       $this->errors[] = "Cannot show private module \"$name\"";
+                                       $this->errors[] = "Cannot build private module \"$name\"";
                                        continue;
                                }
                                $modules[$name] = $module;
@@ -1228,8 +1273,7 @@ MESSAGE;
        /**
         * Returns JS code which, when called, will register a given list of messages.
         *
-        * @param mixed $messages Either an associative array mapping message key to value, or a
-        *   JSON-encoded message blob containing the same data, wrapped in an XmlJsCode object.
+        * @param mixed $messages Associative array mapping message key to value.
         * @return string JavaScript code
         */
        public static function makeMessageSetScript( $messages ) {
@@ -1278,7 +1322,7 @@ MESSAGE;
         *
         * @internal
         * @since 1.32
-        * @param bool|string|array $data
+        * @param mixed $data
         * @return string JSON
         */
        public static function encodeJsonForScript( $data ) {