resourceloader: Tidy up RL to simplify ResourceLoaderEditToolbarModule
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoaderFileModule.php
index 3224b13..0df2892 100644 (file)
@@ -152,6 +152,12 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         */
        protected $localFileRefs = array();
 
+       /**
+        * @var array Place where readStyleFile() tracks file dependencies for non-existent files.
+        * Used in tests to detect missing dependencies.
+        */
+       protected $missingLocalFileRefs = array();
+
        /* Methods */
 
        /**
@@ -411,21 +417,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                );
                // Collect referenced files
                $this->localFileRefs = array_unique( $this->localFileRefs );
-               // If the list has been modified since last time we cached it, update the cache
-               try {
-                       if ( $this->localFileRefs !== $this->getFileDependencies( $context->getSkin() ) ) {
-                               $dbw = wfGetDB( DB_MASTER );
-                               $dbw->replace( 'module_deps',
-                                       array( array( 'md_module', 'md_skin' ) ), array(
-                                               'md_module' => $this->getName(),
-                                               'md_skin' => $context->getSkin(),
-                                               'md_deps' => FormatJson::encode( $this->localFileRefs ),
-                                       )
-                               );
-                       }
-               } catch ( Exception $e ) {
-                       wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
-               }
+               $this->saveFileDependencies( $context->getSkin(), $this->localFileRefs );
+
                return $styles;
        }
 
@@ -591,10 +584,14 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
 
                $options = array();
                foreach ( array(
-                       // T104950: Do not include localBasePath or remoteBasePath!
-                       // Those paths may vary over time and needlessly invalidate cache. If the path changes
-                       // in a way that makes relative file paths point to something else, getFileHashes() will
-                       // account for that already.
+                       // The following properties are omitted because they don't affect the module reponse:
+                       // - localBasePath (Per T104950; Changes when absolute directory name changes. If
+                       //    this affects 'scripts' and other file paths, getFileHashes accounts for that.)
+                       // - remoteBasePath (Per T104950)
+                       // - dependencies (provided via startup module)
+                       // - targets
+                       // - group (provided via startup module)
+                       // - position (only used by OutputPage)
                        'scripts',
                        'debugScripts',
                        'loaderScripts',
@@ -602,12 +599,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        'languageScripts',
                        'skinScripts',
                        'skinStyles',
-                       'dependencies',
                        'messages',
-                       'targets',
                        'templates',
-                       'group',
-                       'position',
                        'skipFunction',
                        'debugRaw',
                        'raw',
@@ -861,13 +854,21 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @param array $styles List of media type/list of file paths pairs, to read, remap and
         * concetenate
         * @param bool $flip
-        * @param ResourceLoaderContext $context (optional)
+        * @param ResourceLoaderContext $context
         *
         * @throws MWException
         * @return array List of concatenated and remapped CSS data from $styles,
         *     keyed by media type
+        *
+        * @since 1.26 Calling this method without a ResourceLoaderContext instance
+        *   is deprecated.
         */
        public function readStyleFiles( array $styles, $flip, $context = null ) {
+               if ( $context === null ) {
+                       wfDeprecated( __METHOD__ . ' without a ResourceLoader context', '1.26' );
+                       $context = ResourceLoaderContext::newDummyContext();
+               }
+
                if ( empty( $styles ) ) {
                        return array();
                }
@@ -889,12 +890,12 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *
         * @param string $path File path of style file to read
         * @param bool $flip
-        * @param ResourceLoaderContext $context (optional)
+        * @param ResourceLoaderContext $context
         *
         * @return string CSS data in script file
         * @throws MWException If the file doesn't exist
         */
-       protected function readStyleFile( $path, $flip, $context = null ) {
+       protected function readStyleFile( $path, $flip, $context ) {
                $localPath = $this->getLocalPath( $path );
                $remotePath = $this->getRemotePath( $path );
                if ( !file_exists( $localPath ) ) {
@@ -904,8 +905,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                }
 
                if ( $this->getStyleSheetLang( $localPath ) === 'less' ) {
-                       $compiler = $this->getLessCompiler( $context );
-                       $style = $this->compileLessFile( $localPath, $compiler );
+                       $style = $this->compileLessFile( $localPath, $context );
                        $this->hasGeneratedStyles = true;
                } else {
                        $style = file_get_contents( $localPath );
@@ -917,10 +917,14 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                $localDir = dirname( $localPath );
                $remoteDir = dirname( $remotePath );
                // Get and register local file references
-               $this->localFileRefs = array_merge(
-                       $this->localFileRefs,
-                       CSSMin::getLocalFileReferences( $style, $localDir )
-               );
+               $localFileRefs = CSSMin::getAllLocalFileReferences( $style, $localDir );
+               foreach ( $localFileRefs as $file ) {
+                       if ( file_exists( $file ) ) {
+                               $this->localFileRefs[] = $file;
+                       } else {
+                               $this->missingLocalFileRefs[] = $file;
+                       }
+               }
                return CSSMin::remap(
                        $style, $localDir, $remoteDir, true
                );
@@ -950,32 +954,50 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * Keeps track of all used files and adds them to localFileRefs.
         *
         * @since 1.22
-        * @throws Exception If lessc encounters a parse error
+        * @since 1.26 Added $context paramter.
+        * @throws Exception If less.php encounters a parse error
         * @param string $fileName File path of LESS source
-        * @param lessc $compiler Compiler to use, if not default
+        * @param ResourceLoaderContext $context Context in which to generate script
         * @return string CSS source
         */
-       protected function compileLessFile( $fileName, $compiler = null ) {
-               if ( !$compiler ) {
-                       $compiler = $this->getLessCompiler();
+       protected function compileLessFile( $fileName, ResourceLoaderContext $context ) {
+               static $cache;
+
+               if ( !$cache ) {
+                       $cache = ObjectCache::newAccelerator( CACHE_ANYTHING );
                }
-               $result = $compiler->compileFile( $fileName );
-               $this->localFileRefs += array_keys( $compiler->allParsedFiles() );
-               return $result;
-       }
 
-       /**
-        * Get a LESS compiler instance for this module in given context.
-        *
-        * Just calls ResourceLoader::getLessCompiler() by default to get a global compiler.
-        *
-        * @param ResourceLoaderContext $context
-        * @throws MWException
-        * @since 1.24
-        * @return lessc
-        */
-       protected function getLessCompiler( ResourceLoaderContext $context = null ) {
-               return ResourceLoader::getLessCompiler( $this->getConfig() );
+               // Construct a cache key from the LESS file name and a hash digest
+               // of the LESS variables used for compilation.
+               $vars = $this->getLessVars( $context );
+               ksort( $vars );
+               $varsHash = hash( 'md4', serialize( $vars ) );
+               $cacheKey = wfGlobalCacheKey( 'LESS', $fileName, $varsHash );
+               $cachedCompile = $cache->get( $cacheKey );
+
+               // If we got a cached value, we have to validate it by getting a
+               // checksum of all the files that were loaded by the parser and
+               // ensuring it matches the cached entry's.
+               if ( isset( $cachedCompile['hash'] ) ) {
+                       $contentHash = FileContentsHasher::getFileContentsHash( $cachedCompile['files'] );
+                       if ( $contentHash === $cachedCompile['hash'] ) {
+                               $this->localFileRefs += $cachedCompile['files'];
+                               return $cachedCompile['css'];
+                       }
+               }
+
+               $compiler = ResourceLoader::getLessCompiler( $this->getConfig(), $vars );
+               $css = $compiler->parseFile( $fileName )->getCss();
+               $files = $compiler->AllParsedFiles();
+               $this->localFileRefs = array_merge( $this->localFileRefs, $files );
+
+               $cache->set( $cacheKey, array(
+                       'css'   => $css,
+                       'files' => $files,
+                       'hash'  => FileContentsHasher::getFileContentsHash( $files ),
+               ), 60 * 60 * 24 );  // 86400 seconds, or 24 hours.
+
+               return $css;
        }
 
        /**