*/
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 */
/**
);
// 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;
}
$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',
'languageScripts',
'skinScripts',
'skinStyles',
- 'dependencies',
'messages',
- 'targets',
'templates',
- 'group',
- 'position',
'skipFunction',
'debugRaw',
'raw',
* @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();
}
*
* @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 ) ) {
}
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 );
$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
);
* 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;
}
/**