*/
private $logger;
+ /** @var string JavaScript / CSS pragma to disable minification. **/
+ const FILTER_NOMIN = ' /* @nomin */ ';
+
/**
* Load information stored in the database about modules.
*
$skin = $context->getSkin();
$lang = $context->getLanguage();
- // Get file dependency information
+ // Batched version of ResourceLoaderModule::getFileDependencies
+ $vary = "$skin|$lang";
$res = $dbr->select( 'module_deps', array( 'md_module', 'md_deps' ), array(
'md_module' => $modules,
- 'md_skin' => $skin
+ 'md_skin' => $vary,
), __METHOD__
);
-
- // Set modules' dependencies
+ // Prime in-object cache values for each module
$modulesWithDeps = array();
foreach ( $res as $row ) {
$module = $this->getModule( $row->md_module );
if ( $module ) {
- $module->setFileDependencies( $skin, FormatJson::decode( $row->md_deps, true ) );
+ $module->setFileDependencies( $context, ResourceLoaderModule::expandRelativePaths(
+ FormatJson::decode( $row->md_deps, true )
+ ) );
$modulesWithDeps[] = $row->md_module;
}
}
-
// Register the absence of a dependency row too
foreach ( array_diff( $modules, $modulesWithDeps ) as $name ) {
$module = $this->getModule( $name );
if ( $module ) {
- $this->getModule( $name )->setFileDependencies( $skin, array() );
+ $this->getModule( $name )->setFileDependencies( $context, array() );
}
}
*
* @param string $filter Name of filter to run
* @param string $data Text to filter, such as JavaScript or CSS text
- * @param array $options For back-compat, can also be the boolean value for "cacheReport". Keys:
+ * @param array $options Keys:
* - (bool) cache: Whether to allow caching this data. Default: true.
- * - (bool) cacheReport: Whether to include the "cache key" report comment. Default: false.
* @return string Filtered data, or a comment containing an error message
*/
- public function filter( $filter, $data, $options = array() ) {
- // Back-compat
- if ( is_bool( $options ) ) {
- $options = array( 'cacheReport' => $options );
- }
- // Defaults
- $options += array( 'cache' => true, 'cacheReport' => false );
- $stats = RequestContext::getMain()->getStats();
-
- // Don't filter empty content
- if ( trim( $data ) === '' ) {
+ public static function filter( $filter, $data, Array $options = array() ) {
+ if ( strpos( $data, ResourceLoader::FILTER_NOMIN ) !== false ) {
return $data;
}
- if ( !in_array( $filter, array( 'minify-js', 'minify-css' ) ) ) {
- $this->logger->warning( 'Invalid filter {filter}', array(
- 'filter' => $filter
- ) );
- return $data;
+ if ( isset( $options['cache'] ) && $options['cache'] === false ) {
+ return self::applyFilter( $filter, $data );
}
- if ( !$options['cache'] ) {
- $result = self::applyFilter( $filter, $data, $this->config );
+ $stats = RequestContext::getMain()->getStats();
+ $cache = ObjectCache::newAccelerator( CACHE_ANYTHING );
+
+ $key = wfGlobalCacheKey(
+ 'resourceloader',
+ 'filter',
+ $filter,
+ self::$filterCacheVersion, md5( $data )
+ );
+
+ $result = $cache->get( $key );
+ if ( $result === false ) {
+ $stats->increment( "resourceloader_cache.$filter.miss" );
+ $result = self::applyFilter( $filter, $data );
+ $cache->set( $key, $result, 24 * 3600 );
} else {
- $key = wfGlobalCacheKey(
- 'resourceloader',
- 'filter',
- $filter,
- self::$filterCacheVersion, md5( $data )
- );
- $cache = wfGetCache( wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING );
- $cacheEntry = $cache->get( $key );
- if ( is_string( $cacheEntry ) ) {
- $stats->increment( "resourceloader_cache.$filter.hit" );
- return $cacheEntry;
- }
- $result = '';
- try {
- $statStart = microtime( true );
- $result = self::applyFilter( $filter, $data, $this->config );
- $statTiming = microtime( true ) - $statStart;
- $stats->increment( "resourceloader_cache.$filter.miss" );
- $stats->timing( "resourceloader_cache.$filter.timing", 1000 * $statTiming );
- if ( $options['cacheReport'] ) {
- $result .= "\n/* cache key: $key */";
- }
- // Set a TTL since HHVM's APC doesn't have any limitation or eviction logic.
- $cache->set( $key, $result, 24 * 3600 );
- } catch ( Exception $e ) {
- MWExceptionHandler::logException( $e );
- $this->logger->warning( 'Minification failed: {exception}', array(
- 'exception' => $e
- ) );
- $this->errors[] = self::formatExceptionNoComment( $e );
- }
+ $stats->increment( "resourceloader_cache.$filter.hit" );
+ }
+ if ( $result === null ) {
+ // Cached failure
+ $result = $data;
}
return $result;
}
- private static function applyFilter( $filter, $data, Config $config ) {
- switch ( $filter ) {
- case 'minify-js':
- return JavaScriptMinifier::minify( $data,
- $config->get( 'ResourceLoaderMinifierStatementsOnOwnLine' ),
- $config->get( 'ResourceLoaderMinifierMaxLineLength' )
- );
- case 'minify-css':
- return CSSMin::minify( $data );
+ private static function applyFilter( $filter, $data ) {
+ $data = trim( $data );
+ if ( $data ) {
+ try {
+ $data = ( $filter === 'minify-css' )
+ ? CSSMin::minify( $data )
+ : JavaScriptMinifier::minify( $data );
+ } catch ( Exception $e ) {
+ MWExceptionHandler::logException( $e );
+ return null;
+ }
}
-
return $data;
}
// Generate output
$isRaw = false;
+
+ $filter = $context->getOnly() === 'styles' ? 'minify-css' : 'minify-js';
+
foreach ( $modules as $name => $module ) {
try {
$content = $module->getModuleContent( $context );
+ $strContent = '';
// Append output
switch ( $context->getOnly() ) {
$scripts = $content['scripts'];
if ( is_string( $scripts ) ) {
// Load scripts raw...
- $out .= $scripts;
+ $strContent = $scripts;
} elseif ( is_array( $scripts ) ) {
// ...except when $scripts is an array of URLs
- $out .= self::makeLoaderImplementScript( $name, $scripts, array(), array(), array() );
+ $strContent = self::makeLoaderImplementScript( $name, $scripts, array(), array(), array() );
}
break;
case '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.
- $out .= isset( $styles['css'] ) ? implode( '', $styles['css'] ) : '';
+ $strContent = isset( $styles['css'] ) ? implode( '', $styles['css'] ) : '';
break;
default:
- $out .= self::makeLoaderImplementScript(
+ $strContent = self::makeLoaderImplementScript(
$name,
isset( $content['scripts'] ) ? $content['scripts'] : '',
isset( $content['styles'] ) ? $content['styles'] : array(),
);
break;
}
+
+ if ( !$context->getDebug() ) {
+ $strContent = self::filter( $filter, $strContent );
+ }
+
+ $out .= $strContent;
+
} catch ( Exception $e ) {
MWExceptionHandler::logException( $e );
$this->logger->warning( 'Generating module package failed: {exception}', array(
// Set the state of modules we didn't respond to with mw.loader.implement
if ( count( $states ) ) {
- $out .= self::makeLoaderStateScript( $states );
+ $stateScript = self::makeLoaderStateScript( $states );
+ if ( !$context->getDebug() ) {
+ $stateScript = self::filter( 'minify-js', $stateScript );
+ }
+ $out .= $stateScript;
}
} else {
if ( count( $states ) ) {
}
}
- $enableFilterCache = true;
- if ( count( $modules ) === 1 && reset( $modules ) instanceof ResourceLoaderUserTokensModule ) {
- // If we're building the embedded user.tokens, don't cache (T84960)
- $enableFilterCache = false;
- }
-
- if ( !$context->getDebug() ) {
- if ( $context->getOnly() === 'styles' ) {
- $out = $this->filter( 'minify-css', $out );
- } else {
- $out = $this->filter( 'minify-js', $out, array(
- 'cache' => $enableFilterCache
- ) );
- }
- }
-
return $out;
}
// Minify manually because the general makeModuleResponse() minification won't be
// effective here due to the script being a string instead of a function. (T107377)
if ( !ResourceLoader::inDebugMode() ) {
- $scripts = self::applyFilter( 'minify-js', $scripts,
- ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
+ $scripts = self::filter( 'minify-js', $scripts );
}
} else {
$scripts = new XmlJsCode( "function ( $, jQuery ) {\n{$scripts}\n}" );
* @return string
*/
public static function makeConfigSetScript( array $configuration ) {
- if ( ResourceLoader::inDebugMode() ) {
- return Xml::encodeJsCall( 'mw.config.set', array( $configuration ), true );
- }
-
- $config = RequestContext::getMain()->getConfig();
- $js = Xml::encodeJsCall( 'mw.config.set', array( $configuration ), false );
- return self::applyFilter( 'minify-js', $js, $config );
+ return Xml::encodeJsCall(
+ 'mw.config.set',
+ array( $configuration ),
+ ResourceLoader::inDebugMode()
+ ) . ResourceLoader::FILTER_NOMIN;
}
/**
* @return bool Whether $moduleName is a valid module name
*/
public static function isValidModuleName( $moduleName ) {
- return !preg_match( '/[|,!]/', $moduleName ) && strlen( $moduleName ) <= 255;
+ return strcspn( $moduleName, '!,|', 0, 255 ) === strlen( $moduleName );
}
/**