diff options
Diffstat (limited to 'includes/resourceloader/ResourceLoaderFileModule.php')
-rw-r--r-- | includes/resourceloader/ResourceLoaderFileModule.php | 176 |
1 files changed, 139 insertions, 37 deletions
diff --git a/includes/resourceloader/ResourceLoaderFileModule.php b/includes/resourceloader/ResourceLoaderFileModule.php index cedb5dcc..9ed181ed 100644 --- a/includes/resourceloader/ResourceLoaderFileModule.php +++ b/includes/resourceloader/ResourceLoaderFileModule.php @@ -116,6 +116,12 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { protected $targets = array( 'desktop' ); /** + * Boolean: Whether getStyleURLsForDebug should return raw file paths, + * or return load.php urls + */ + protected $hasGeneratedStyles = false; + + /** * Array: Cache for mtime * @par Usage: * @code @@ -187,8 +193,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { * @endcode */ public function __construct( $options = array(), $localBasePath = null, - $remoteBasePath = null ) - { + $remoteBasePath = null + ) { global $IP, $wgScriptPath, $wgResourceBasePath; $this->localBasePath = $localBasePath === null ? $IP : $localBasePath; if ( $remoteBasePath !== null ) { @@ -209,7 +215,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { case 'debugScripts': case 'loaderScripts': case 'styles': - $this->{$member} = (array) $option; + $this->{$member} = (array)$option; break; // Collated lists of file paths case 'languageScripts': @@ -228,26 +234,26 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { "'$key' given, string expected." ); } - $this->{$member}[$key] = (array) $value; + $this->{$member}[$key] = (array)$value; } break; // Lists of strings case 'dependencies': case 'messages': case 'targets': - $this->{$member} = (array) $option; + $this->{$member} = (array)$option; break; // Single strings case 'group': case 'position': case 'localBasePath': case 'remoteBasePath': - $this->{$member} = (string) $option; + $this->{$member} = (string)$option; break; // Single booleans case 'debugRaw': case 'raw': - $this->{$member} = (bool) $option; + $this->{$member} = (bool)$option; break; } } @@ -259,8 +265,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets all scripts for a given context concatenated together. * - * @param $context ResourceLoaderContext: Context in which to generate script - * @return String: JavaScript code for $context + * @param ResourceLoaderContext $context Context in which to generate script + * @return string: JavaScript code for $context */ public function getScript( ResourceLoaderContext $context ) { $files = $this->getScriptFiles( $context ); @@ -268,7 +274,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { } /** - * @param $context ResourceLoaderContext + * @param ResourceLoaderContext $context * @return array */ public function getScriptURLsForDebug( ResourceLoaderContext $context ) { @@ -289,7 +295,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets loader script. * - * @return String: JavaScript code to be added to startup module + * @return string: JavaScript code to be added to startup module */ public function getLoaderScript() { if ( count( $this->loaderScripts ) == 0 ) { @@ -301,8 +307,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets all styles for a given context concatenated together. * - * @param $context ResourceLoaderContext: Context in which to generate styles - * @return String: CSS code for $context + * @param ResourceLoaderContext $context Context in which to generate styles + * @return string: CSS code for $context */ public function getStyles( ResourceLoaderContext $context ) { $styles = $this->readStyleFiles( @@ -324,16 +330,23 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { ); } } catch ( Exception $e ) { - wfDebug( __METHOD__ . " failed to update DB: $e\n" ); + wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" ); } return $styles; } /** - * @param $context ResourceLoaderContext + * @param ResourceLoaderContext $context * @return array */ public function getStyleURLsForDebug( ResourceLoaderContext $context ) { + if ( $this->hasGeneratedStyles ) { + // Do the default behaviour of returning a url back to load.php + // but with only=styles. + return parent::getStyleURLsForDebug( $context ); + } + // Our module consists entirely of real css files, + // in debug mode we can load those directly. $urls = array(); foreach ( $this->getStyleFiles( $context ) as $mediaType => $list ) { $urls[$mediaType] = array(); @@ -347,7 +360,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets list of message keys used by this module. * - * @return Array: List of message keys + * @return array: List of message keys */ public function getMessages() { return $this->messages; @@ -356,7 +369,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets the name of the group this module should be loaded in. * - * @return String: Group name + * @return string: Group name */ public function getGroup() { return $this->group; @@ -372,7 +385,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets list of names of modules this module depends on. * - * @return Array: List of module names + * @return array: List of module names */ public function getDependencies() { return $this->dependencies; @@ -394,9 +407,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { * calculations on files relevant to the given language, skin and debug * mode. * - * @param $context ResourceLoaderContext: Context in which to calculate + * @param ResourceLoaderContext $context Context in which to calculate * the modified time - * @return Integer: UNIX timestamp + * @return int: UNIX timestamp * @see ResourceLoaderModule::getFileDependencies */ public function getModifiedTime( ResourceLoaderContext $context ) { @@ -455,7 +468,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /* Protected Methods */ /** - * @param $path string + * @param string $path * @return string */ protected function getLocalPath( $path ) { @@ -463,7 +476,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { } /** - * @param $path string + * @param string $path * @return string */ protected function getRemotePath( $path ) { @@ -471,17 +484,28 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { } /** + * Infer the stylesheet language from a stylesheet file path. + * + * @since 1.22 + * @param string $path + * @return string: the stylesheet language name + */ + public function getStyleSheetLang( $path ) { + return preg_match( '/\.less$/i', $path ) ? 'less' : 'css'; + } + + /** * Collates file paths by option (where provided). * * @param array $list List of file paths in any combination of index/path * or path/options pairs * @param string $option option name - * @param $default Mixed: default value if the option isn't set - * @return Array: List of file paths, collated by $option + * @param mixed $default default value if the option isn't set + * @return array: List of file paths, collated by $option */ protected static function collateFilePathListByOption( array $list, $option, $default ) { $collatedFiles = array(); - foreach ( (array) $list as $key => $value ) { + foreach ( (array)$list as $key => $value ) { if ( is_int( $key ) ) { // File name as the value if ( !isset( $collatedFiles[$default] ) ) { @@ -506,7 +530,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { * @param array $list List of lists to select from * @param string $key Key to look for in $map * @param string $fallback Key to look for in $list if $key doesn't exist - * @return Array: List of elements from $map which matched $key or $fallback, + * @return array: List of elements from $map which matched $key or $fallback, * or an empty list in case of no match */ protected static function tryForKey( array $list, $key, $fallback = null ) { @@ -524,8 +548,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets a list of file paths for all scripts in this module, in order of propper execution. * - * @param $context ResourceLoaderContext: Context - * @return Array: List of file paths + * @param ResourceLoaderContext $context + * @return array: List of file paths */ protected function getScriptFiles( ResourceLoaderContext $context ) { $files = array_merge( @@ -543,8 +567,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Gets a list of file paths for all styles in this module, in order of propper inclusion. * - * @param $context ResourceLoaderContext: Context - * @return Array: List of file paths + * @param ResourceLoaderContext $context + * @return array: List of file paths */ protected function getStyleFiles( ResourceLoaderContext $context ) { return array_merge_recursive( @@ -556,11 +580,28 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { } /** + * Returns all style files used by this module + * @return array + */ + public function getAllStyleFiles() { + $files = array(); + foreach( (array)$this->styles as $key => $value ) { + if ( is_array( $value ) ) { + $path = $key; + } else { + $path = $value; + } + $files[] = $this->getLocalPath( $path ); + } + return $files; + } + + /** * Gets the contents of a list of JavaScript files. * * @param array $scripts List of file paths to scripts to read, remap and concetenate * @throws MWException - * @return String: Concatenated and remapped JavaScript data from $scripts + * @return string: Concatenated and remapped JavaScript data from $scripts */ protected function readScriptFiles( array $scripts ) { global $wgResourceLoaderValidateStaticJS; @@ -591,9 +632,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { * @param array $styles List of media type/list of file paths pairs, to read, remap and * concetenate * - * @param $flip bool + * @param bool $flip * - * @return Array: List of concatenated and remapped CSS data from $styles, + * @return array: List of concatenated and remapped CSS data from $styles, * keyed by media type */ protected function readStyleFiles( array $styles, $flip ) { @@ -620,9 +661,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { * This method can be used as a callback for array_map() * * @param string $path File path of style file to read - * @param $flip bool + * @param bool $flip * - * @return String: CSS data in script file + * @return string: CSS data in script file * @throws MWException if the file doesn't exist */ protected function readStyleFile( $path, $flip ) { @@ -632,7 +673,14 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { wfDebugLog( 'resourceloader', $msg ); throw new MWException( $msg ); } - $style = file_get_contents( $localPath ); + + if ( $this->getStyleSheetLang( $path ) === 'less' ) { + $style = $this->compileLESSFile( $localPath ); + $this->hasGeneratedStyles = true; + } else { + $style = file_get_contents( $localPath ); + } + if ( $flip ) { $style = CSSJanus::transform( $style, true, false ); } @@ -655,7 +703,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { /** * Get whether CSS for this module should be flipped - * @param $context ResourceLoaderContext + * @param ResourceLoaderContext $context * @return bool */ public function getFlip( $context ) { @@ -671,4 +719,58 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { return $this->targets; } + /** + * Generate a cache key for a LESS file. + * + * The cache key varies on the file name and the names and values of global + * LESS variables. + * + * @since 1.22 + * @param string $fileName File name of root LESS file. + * @return string: Cache key + */ + protected static function getLESSCacheKey( $fileName ) { + $vars = json_encode( ResourceLoader::getLESSVars() ); + $hash = md5( $fileName . $vars ); + return wfMemcKey( 'resourceloader', 'less', $hash ); + } + + /** + * Compile a LESS file into CSS. + * + * If invalid, returns replacement CSS source consisting of the compilation + * error message encoded as a comment. To save work, we cache a result object + * which comprises the compiled CSS and the names & mtimes of the files + * that were processed. lessphp compares the cached & current mtimes and + * recompiles as necessary. + * + * @since 1.22 + * @param string $fileName File path of LESS source + * @return string: CSS source + */ + protected function compileLESSFile( $fileName ) { + $key = self::getLESSCacheKey( $fileName ); + $cache = wfGetCache( CACHE_ANYTHING ); + + // The input to lessc. Either an associative array representing the + // cached results of a previous compilation, or the string file name if + // no cache result exists. + $source = $cache->get( $key ); + if ( !is_array( $source ) || !isset( $source['root'] ) ) { + $source = $fileName; + } + + $compiler = ResourceLoader::getLessCompiler(); + $result = null; + + $result = $compiler->cachedCompile( $source ); + + if ( !is_array( $result ) ) { + throw new MWException( 'LESS compiler result has type ' . gettype( $result ) . '; array expected.' ); + } + + $this->localFileRefs += array_keys( $result['files'] ); + $cache->set( $key, $result ); + return $result['compiled']; + } } |