X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fresourceloader%2FResourceLoaderModule.php;h=d4dabe7d71b25a689952023452caae9f3541b6ad;hb=2f28050b6b866f684da92c1832c0a3938b59ef0b;hp=8124f3398b22797ff1ccf120f1c2eeccf1635eee;hpb=4fadea72334cba2b8562a8a419665318030d3145;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 8124f3398b..743b69b3fe 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -147,8 +147,8 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { if ( $deprecationInfo ) { $name = $this->getName(); $warning = 'This page is using the deprecated ResourceLoader module "' . $name . '".'; - if ( !is_bool( $deprecationInfo ) && isset( $deprecationInfo['message'] ) ) { - $warning .= "\n" . $deprecationInfo['message']; + if ( is_string( $deprecationInfo ) ) { + $warning .= "\n" . $deprecationInfo; } return Xml::encodeJsCall( 'mw.log.warn', @@ -461,29 +461,47 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { * @param array $localFileRefs List of files */ protected function saveFileDependencies( ResourceLoaderContext $context, $localFileRefs ) { - // Normalise array - $localFileRefs = array_values( array_unique( $localFileRefs ) ); - sort( $localFileRefs ); try { + // Related bugs and performance considerations: + // 1. Don't needlessly change the database value with the same list in a + // different order or with duplicates. + // 2. Use relative paths to avoid ghost entries when $IP changes. (T111481) + // 3. Don't needlessly replace the database with the same value + // just because $IP changed (e.g. when upgrading a wiki). + // 4. Don't create an endless replace loop on every request for this + // module when '../' is used anywhere. Even though both are expanded + // (one expanded by getFileDependencies from the DB, the other is + // still raw as originally read by RL), the latter has not + // been normalized yet. + + // Normalise + $localFileRefs = array_values( array_unique( $localFileRefs ) ); + sort( $localFileRefs ); + $localPaths = self::getRelativePaths( $localFileRefs ); + + $storedPaths = self::getRelativePaths( $this->getFileDependencies( $context ) ); // If the list has been modified since last time we cached it, update the cache - if ( $localFileRefs !== $this->getFileDependencies( $context ) ) { + if ( $localPaths !== $storedPaths ) { + $vary = $context->getSkin() . '|' . $context->getLanguage(); $cache = ObjectCache::getLocalClusterInstance(); - $key = $cache->makeKey( __METHOD__, $this->getName() ); + $key = $cache->makeKey( __METHOD__, $this->getName(), $vary ); $scopeLock = $cache->getScopedLock( $key, 0 ); if ( !$scopeLock ) { return; // T124649; avoid write slams } - $vary = $context->getSkin() . '|' . $context->getLanguage(); + $deps = FormatJson::encode( $localPaths ); $dbw = wfGetDB( DB_MASTER ); - $dbw->replace( 'module_deps', - [ [ 'md_module', 'md_skin' ] ], + $dbw->upsert( 'module_deps', [ 'md_module' => $this->getName(), 'md_skin' => $vary, - // Use relative paths to avoid ghost entries when $IP changes (T111481) - 'md_deps' => FormatJson::encode( self::getRelativePaths( $localFileRefs ) ), + 'md_deps' => $deps, + ], + [ 'md_module', 'md_skin' ], + [ + 'md_deps' => $deps, ] ); @@ -606,7 +624,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { */ final protected function buildContent( ResourceLoaderContext $context ) { $rl = $context->getResourceLoader(); - $stats = RequestContext::getMain()->getStats(); + $stats = MediaWikiServices::getInstance()->getStatsdDataFactory(); $statStart = microtime( true ); // Only include properties that are relevant to this context (e.g. only=scripts) @@ -625,16 +643,18 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { $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. + // Make the script safe to concatenate by making sure there is at least one + // trailing new line at the end of the content. Previously, this looked for + // a semi-colon instead, but that breaks concatenation if the semicolon + // is inside a comment like "// foo();". Instead, simply use a + // line break as separator which matches JavaScript native logic for implicitly + // ending statements even if a semi-colon is missing. + // Bugs: T29054, T162719. if ( is_string( $scripts ) && strlen( $scripts ) - && substr( rtrim( $scripts ), -1 ) !== ';' + && substr( $scripts, -1 ) !== "\n" ) { - // Append semicolon to prevent weird bugs caused by files not - // terminating their statements right (bug 27054) - $scripts .= ";\n"; + $scripts .= "\n"; } } $content['scripts'] = $scripts; @@ -644,7 +664,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { if ( $context->shouldIncludeStyles() ) { $styles = []; // Don't create empty stylesheets like [ '' => '' ] for modules - // that don't *have* any stylesheets (bug 38024). + // that don't *have* any stylesheets (T40024). $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 @@ -825,7 +845,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { */ public function getDefinitionSummary( ResourceLoaderContext $context ) { return [ - '_class' => get_class( $this ), + '_class' => static::class, '_cacheEpoch' => $this->getConfig()->get( 'CacheEpoch' ), ]; } @@ -913,36 +933,33 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface { * @return string JS with the original, or a replacement error */ protected function validateScriptFile( $fileName, $contents ) { - if ( $this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) { - // Try for cache hit - $cache = ObjectCache::getMainWANInstance(); - $key = $cache->makeKey( + if ( !$this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) { + return $contents; + } + $cache = ObjectCache::getMainWANInstance(); + return $cache->getWithSetCallback( + $cache->makeGlobalKey( 'resourceloader', 'jsparse', self::$parseCacheVersion, - md5( $contents ) - ); - $cacheEntry = $cache->get( $key ); - if ( is_string( $cacheEntry ) ) { - return $cacheEntry; - } - - $parser = self::javaScriptParser(); - try { - $parser->parse( $contents, $fileName, 1 ); - $result = $contents; - } catch ( Exception $e ) { - // We'll save this to cache to avoid having to validate broken JS over and over... - $err = $e->getMessage(); - $result = "mw.log.error(" . - Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");"; + md5( $contents ), + $fileName + ), + $cache::TTL_WEEK, + function () use ( $contents, $fileName ) { + $parser = self::javaScriptParser(); + try { + $parser->parse( $contents, $fileName, 1 ); + $result = $contents; + } catch ( Exception $e ) { + // We'll save this to cache to avoid having to re-validate broken JS + $err = $e->getMessage(); + $result = "mw.log.error(" . + Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");"; + } + return $result; } - - $cache->set( $key, $result ); - return $result; - } else { - return $contents; - } + ); } /**