resourceloader: Replace timestamp system with version hashing
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoaderFileModule.php
index fbca08e..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."
                                                        );
@@ -295,6 +286,18 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                }
                if ( $hasTemplates ) {
                        $this->dependencies[] = 'mediawiki.template';
+                       // Ensure relevant template compiler module gets loaded
+                       foreach ( $this->templates as $alias => $templatePath ) {
+                               if ( is_int( $alias ) ) {
+                                       $alias = $templatePath;
+                               }
+                               $suffix = explode( '.', $alias );
+                               $suffix = end( $suffix );
+                               $compilerModule = 'mediawiki.template.' . $suffix;
+                               if ( $suffix !== 'html' && !in_array( $compilerModule, $this->dependencies ) ) {
+                                       $this->dependencies[] = $compilerModule;
+                               }
+                       }
                }
        }
 
@@ -510,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
@@ -518,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
@@ -561,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 );
        }
 
        /**
@@ -587,9 +580,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @return array
         */
        public function getDefinitionSummary( ResourceLoaderContext $context ) {
-               $summary = array(
-                       'class' => get_class( $this ),
-               );
+               $summary = parent::getDefinitionSummary( $context );
+
+               $options = array();
                foreach ( array(
                        'scripts',
                        'debugScripts',
@@ -605,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