resourceloader: Replace timestamp system with version hashing
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoaderFileModule.php
index a46c931..3569bf3 100644 (file)
@@ -143,15 +143,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         */
        protected $hasGeneratedStyles = false;
 
-       /**
-        * @var array Cache for mtime
-        * @par Usage:
-        * @code
-        * array( [hash] => [mtime], [hash] => [mtime], ... )
-        * @endcode
-        */
-       protected $modifiedTime = array();
-
        /**
         * @var array Place where readStyleFile() tracks file dependencies
         * @par Usage:
@@ -174,7 +165,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *     to $wgResourceBasePath
         *
         * Below is a description for the $options array:
-        * @throws MWException
+        * @throws InvalidArgumentException
         * @par Construction options:
         * @code
         *     array(
@@ -255,14 +246,14 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                case 'skinScripts':
                                case 'skinStyles':
                                        if ( !is_array( $option ) ) {
-                                               throw new MWException(
+                                               throw new InvalidArgumentException(
                                                        "Invalid collated file path list error. " .
                                                        "'$option' given, array expected."
                                                );
                                        }
                                        foreach ( $option as $key => $value ) {
                                                if ( !is_string( $key ) ) {
-                                                       throw new MWException(
+                                                       throw new InvalidArgumentException(
                                                                "Invalid collated file path list key error. " .
                                                                "'$key' given, string expected."
                                                        );
@@ -522,7 +513,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
        }
 
        /**
-        * Get the last modified timestamp of this module.
+        * Helper method to gather file mtimes for getDefinitionSummary.
         *
         * Last modified timestamps are calculated from the highest last modified
         * timestamp of this module's constituent files as well as the files it
@@ -530,16 +521,11 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * calculations on files relevant to the given language, skin and debug
         * mode.
         *
-        * @param ResourceLoaderContext $context Context in which to calculate
-        *     the modified time
-        * @return int UNIX timestamp
         * @see ResourceLoaderModule::getFileDependencies
+        * @param ResourceLoaderContext $context
+        * @return array
         */
-       public function getModifiedTime( ResourceLoaderContext $context ) {
-               if ( isset( $this->modifiedTime[$context->getHash()] ) ) {
-                       return $this->modifiedTime[$context->getHash()];
-               }
-
+       protected function getFileMtimes( ResourceLoaderContext $context ) {
                $files = array();
 
                // Flatten style files into $files
@@ -573,23 +559,18 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                $files = array_map( array( $this, 'getLocalPath' ), $files );
                // File deps need to be treated separately because they're already prefixed
                $files = array_merge( $files, $this->getFileDependencies( $context->getSkin() ) );
+               // Filter out any duplicates from getFileDependencies() and others.
+               // Most commonly introduced by compileLessFile(), which always includes the
+               // entry point Less file we already know about.
+               $files = array_values( array_unique( $files ) );
 
-               // If a module is nothing but a list of dependencies, we need to avoid
-               // giving max() an empty array
-               if ( count( $files ) === 0 ) {
-                       $this->modifiedTime[$context->getHash()] = 1;
-                       return $this->modifiedTime[$context->getHash()];
-               }
-
-               $filesMtime = max( array_map( array( __CLASS__, 'safeFilemtime' ), $files ) );
-
-               $this->modifiedTime[$context->getHash()] = max(
-                       $filesMtime,
-                       $this->getMsgBlobMtime( $context->getLanguage() ),
-                       $this->getDefinitionMtime( $context )
-               );
-
-               return $this->modifiedTime[$context->getHash()];
+               // Don't max() because older files are significant.
+               // While the associated file names are significant, that is already taken care of by the
+               // definition summary. Avoid creating an array keyed by file path here because those are
+               // absolute file paths. Including that would needlessly cause global cache invalidation
+               // when the MediaWiki installation path changes (which is quite common in cases like
+               // Wikimedia where the installation path reflects the MediaWiki branch name).
+               return array_map( array( __CLASS__, 'safeFilemtime' ), $files );
        }
 
        /**
@@ -600,6 +581,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         */
        public function getDefinitionSummary( ResourceLoaderContext $context ) {
                $summary = parent::getDefinitionSummary( $context );
+
+               $options = array();
                foreach ( array(
                        'scripts',
                        'debugScripts',
@@ -615,18 +598,24 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        'group',
                        'position',
                        'skipFunction',
+                       // FIXME: localBasePath includes the MediaWiki installation path and
+                       // needlessly causes cache invalidation.
                        'localBasePath',
                        'remoteBasePath',
                        'debugRaw',
                        'raw',
                ) as $member ) {
-                       $summary[$member] = $this->{$member};
+                       $options[$member] = $this->{$member};
                };
+
+               $summary[] = array(
+                       'options' => $options,
+                       'fileMtimes' => $this->getFileMTimes( $context ),
+                       'msgBlobMtime' => $this->getMsgBlobMtime( $context->getLanguage() ),
+               );
                return $summary;
        }
 
-       /* Protected Methods */
-
        /**
         * @param string|ResourceLoaderFilePath $path
         * @return string