From: Timo Tijhof Date: Thu, 14 May 2015 19:05:47 +0000 (+0100) Subject: resourceloader: Move packaging to a new getModuleContent() method X-Git-Tag: 1.31.0-rc.0~11143^2 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/comptes/ajouter.php?a=commitdiff_plain;h=5ddd7f91c7677ae44e57bdafece9ae6bd35b7830;p=lhc%2Fweb%2Fwiklou.git resourceloader: Move packaging to a new getModuleContent() method Centralise the building of module packages into this method so that it can be easily re-used. This is in preparation for providing the option for modules to use content-based hashing (instead of based on meta-data) in the version string of modules. Having a getModuleContent method allows that to be implemented in a way that is well-cached (as we'll be calling it from multiple multiple code paths) and without duplicating this logic. Bug: T98087 Change-Id: I376233caaabe44b6101565b70a50904abdf8ab4f --- diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index d5b17d8658..bbe55ab673 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -298,8 +298,16 @@ class ResourceLoader implements LoggerAwareInterface { } /** - * @param MessageBlobStore $blobStore + * @since 1.26 + * @return MessageBlobStore + */ + public function getMessageBlobStore() { + return $this->blobStore; + } + + /** * @since 1.25 + * @param MessageBlobStore $blobStore */ public function setMessageBlobStore( MessageBlobStore $blobStore ) { $this->blobStore = $blobStore; @@ -962,7 +970,7 @@ MESSAGE; // Pre-fetch blobs if ( $context->shouldIncludeMessages() ) { try { - $blobs = $this->blobStore->get( $this, $modules, $context->getLanguage() ); + $this->blobStore->get( $this, $modules, $context->getLanguage() ); } catch ( Exception $e ) { MWExceptionHandler::logException( $e ); $this->logger->warning( 'Prefetching MessageBlobStore failed: {exception}', array( @@ -970,8 +978,6 @@ MESSAGE; ) ); $this->errors[] = self::formatExceptionNoComment( $e ); } - } else { - $blobs = array(); } foreach ( $missing as $name ) { @@ -981,79 +987,13 @@ MESSAGE; // Generate output $isRaw = false; foreach ( $modules as $name => $module ) { - /** - * @var $module ResourceLoaderModule - */ - try { - $scripts = ''; - if ( $context->shouldIncludeScripts() ) { - // If we are in debug mode, we'll want to return an array of URLs if possible - // However, we can't do this if the module doesn't support it - // We also can't do this if there is an only= parameter, because we have to give - // the module a way to return a load.php URL without causing an infinite loop - if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) { - $scripts = $module->getScriptURLsForDebug( $context ); - } else { - $scripts = $module->getScript( $context ); - // rtrim() because there are usually a few line breaks - // after the last ';'. A new line at EOF, a new line - // added by ResourceLoaderFileModule::readScriptFiles, etc. - if ( is_string( $scripts ) - && strlen( $scripts ) - && substr( rtrim( $scripts ), -1 ) !== ';' - ) { - // Append semicolon to prevent weird bugs caused by files not - // terminating their statements right (bug 27054) - $scripts .= ";\n"; - } - } - } - // Styles - $styles = array(); - if ( $context->shouldIncludeStyles() ) { - // Don't create empty stylesheets like array( '' => '' ) for modules - // that don't *have* any stylesheets (bug 38024). - $stylePairs = $module->getStyles( $context ); - if ( count( $stylePairs ) ) { - // If we are in debug mode without &only= set, we'll want to return an array of URLs - // See comment near shouldIncludeScripts() for more details - if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) { - $styles = array( - 'url' => $module->getStyleURLsForDebug( $context ) - ); - } else { - // Minify CSS before embedding in mw.loader.implement call - // (unless in debug mode) - if ( !$context->getDebug() ) { - foreach ( $stylePairs as $media => $style ) { - // Can be either a string or an array of strings. - if ( is_array( $style ) ) { - $stylePairs[$media] = array(); - foreach ( $style as $cssText ) { - if ( is_string( $cssText ) ) { - $stylePairs[$media][] = $this->filter( 'minify-css', $cssText ); - } - } - } elseif ( is_string( $style ) ) { - $stylePairs[$media] = $this->filter( 'minify-css', $style ); - } - } - } - // Wrap styles into @media groups as needed and flatten into a numerical array - $styles = array( - 'css' => self::makeCombinedStyles( $stylePairs ) - ); - } - } - } - - // Messages - $messagesBlob = isset( $blobs[$name] ) ? $blobs[$name] : '{}'; + $content = $module->getModuleContent( $context ); // Append output switch ( $context->getOnly() ) { case 'scripts': + $scripts = $content['scripts']; if ( is_string( $scripts ) ) { // Load scripts raw... $out .= $scripts; @@ -1063,6 +1003,7 @@ MESSAGE; } break; case 'styles': + $styles = $content['styles']; // We no longer seperate into media, they are all combined now with // custom media type groups into @media .. {} sections as part of the css string. // Module returns either an empty array or a numerical array with css strings. @@ -1071,10 +1012,10 @@ MESSAGE; default: $out .= self::makeLoaderImplementScript( $name, - $scripts, - $styles, - new XmlJsCode( $messagesBlob ), - $module->getTemplates() + isset( $content['scripts'] ) ? $content['scripts'] : '', + isset( $content['styles'] ) ? $content['styles'] : array(), + isset( $content['messagesBlob'] ) ? new XmlJsCode( $content['messagesBlob'] ) : array(), + isset( $content['templates'] ) ? $content['templates'] : array() ); break; } diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 958990cc7f..8d4804cf10 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -64,6 +64,8 @@ abstract class ResourceLoaderModule { protected $msgBlobMtime = array(); // In-object cache for version hash protected $versionHash = array(); + // In-object cache for module content + protected $contents = array(); // Whether the position returned by getPosition() is defined in the module configuration // and not a default value @@ -439,6 +441,122 @@ abstract class ResourceLoaderModule { $this->msgBlobMtime[$lang] = $mtime; } + /** + * Get an array of this module's resources. Ready for serving to the web. + * + * @since 1.26 + * @param ResourceLoaderContext $context + * @return array + */ + public function getModuleContent( ResourceLoaderContext $context ) { + $contextHash = $context->getHash(); + // Cache this expensive operation. This calls builds the scripts, styles, and messages + // content which typically involves filesystem and/or database access. + if ( !array_key_exists( $contextHash, $this->contents ) ) { + $this->contents[ $contextHash ] = $this->buildContent( $context ); + } + return $this->contents[ $contextHash ]; + } + + /** + * Bundle all resources attached to this module into an array. + * + * @since 1.26 + * @param ResourceLoaderContext $context + * @return array + */ + final protected function buildContent( ResourceLoaderContext $context ) { + $rl = $context->getResourceLoader(); + + // Only include properties that are relevant to this context (e.g. only=scripts) + // and that are non-empty (e.g. don't include "templates" for modules without + // templates). This helps prevent invalidating cache for all modules when new + // optional properties are introduced. + $content = array(); + + // Scripts + if ( $context->shouldIncludeScripts() ) { + // If we are in debug mode, we'll want to return an array of URLs if possible + // However, we can't do this if the module doesn't support it + // We also can't do this if there is an only= parameter, because we have to give + // the module a way to return a load.php URL without causing an infinite loop + if ( $context->getDebug() && !$context->getOnly() && $this->supportsURLLoading() ) { + $scripts = $this->getScriptURLsForDebug( $context ); + } else { + $scripts = $this->getScript( $context ); + // rtrim() because there are usually a few line breaks + // after the last ';'. A new line at EOF, a new line + // added by ResourceLoaderFileModule::readScriptFiles, etc. + if ( is_string( $scripts ) + && strlen( $scripts ) + && substr( rtrim( $scripts ), -1 ) !== ';' + ) { + // Append semicolon to prevent weird bugs caused by files not + // terminating their statements right (bug 27054) + $scripts .= ";\n"; + } + } + $content['scripts'] = $scripts; + } + + // Styles + if ( $context->shouldIncludeStyles() ) { + $styles = array(); + // Don't create empty stylesheets like array( '' => '' ) for modules + // that don't *have* any stylesheets (bug 38024). + $stylePairs = $this->getStyles( $context ); + if ( count( $stylePairs ) ) { + // If we are in debug mode without &only= set, we'll want to return an array of URLs + // See comment near shouldIncludeScripts() for more details + if ( $context->getDebug() && !$context->getOnly() && $this->supportsURLLoading() ) { + $styles = array( + 'url' => $this->getStyleURLsForDebug( $context ) + ); + } else { + // Minify CSS before embedding in mw.loader.implement call + // (unless in debug mode) + if ( !$context->getDebug() ) { + foreach ( $stylePairs as $media => $style ) { + // Can be either a string or an array of strings. + if ( is_array( $style ) ) { + $stylePairs[$media] = array(); + foreach ( $style as $cssText ) { + if ( is_string( $cssText ) ) { + $stylePairs[$media][] = $rl->filter( 'minify-css', $cssText ); + } + } + } elseif ( is_string( $style ) ) { + $stylePairs[$media] = $rl->filter( 'minify-css', $style ); + } + } + } + // Wrap styles into @media groups as needed and flatten into a numerical array + $styles = array( + 'css' => $rl->makeCombinedStyles( $stylePairs ) + ); + } + } + $content['styles'] = $styles; + } + + // Messages + $blobs = $rl->getMessageBlobStore()->get( + $rl, + array( $this->getName() => $this ), + $context->getLanguage() + ); + if ( isset( $blobs[$this->getName()] ) ) { + $content['messagesBlob'] = $blobs[$this->getName()]; + } + + $templates = $this->getTemplates(); + if ( $templates ) { + $content['templates'] = $templates; + } + + return $content; + } + /** * Get a string identifying the current version of this module in a given context. *