Merge "Declare and document $wgStatsdServer and $wgStatsdMetricPrefix"
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoaderModule.php
index ec7ed70..94edb36 100644 (file)
@@ -293,9 +293,9 @@ abstract class ResourceLoaderModule {
 
        /**
         * Whether the position returned by getPosition() is a default value or comes from the module
-        * definition. This method is meant to be short-lived, and is only useful until classes added via
-        * addModuleStyles with a default value define an explicit position. See getModuleStyles() in
-        * OutputPage for the related migration warning.
+        * definition. This method is meant to be short-lived, and is only useful until classes added
+        * via addModuleStyles with a default value define an explicit position. See getModuleStyles()
+        * in OutputPage for the related migration warning.
         *
         * @return bool
         * @since  1.26
@@ -386,16 +386,21 @@ abstract class ResourceLoaderModule {
                }
 
                $dbr = wfGetDB( DB_SLAVE );
-               $deps = $dbr->selectField( 'module_deps', 'md_deps', array(
+               $deps = $dbr->selectField( 'module_deps',
+                       'md_deps',
+                       array(
                                'md_module' => $this->getName(),
                                'md_skin' => $skin,
-                       ), __METHOD__
+                       ),
+                       __METHOD__
                );
+
                if ( !is_null( $deps ) ) {
                        $this->fileDeps[$skin] = (array)FormatJson::decode( $deps, true );
                } else {
                        $this->fileDeps[$skin] = array();
                }
+
                return $this->fileDeps[$skin];
        }
 
@@ -421,10 +426,13 @@ abstract class ResourceLoaderModule {
                        }
 
                        $dbr = wfGetDB( DB_SLAVE );
-                       $msgBlobMtime = $dbr->selectField( 'msg_resource', 'mr_timestamp', array(
+                       $msgBlobMtime = $dbr->selectField( 'msg_resource',
+                               'mr_timestamp',
+                               array(
                                        'mr_resource' => $this->getName(),
                                        'mr_lang' => $lang
-                               ), __METHOD__
+                               ),
+                               __METHOD__
                        );
                        // If no blob was found, but the module does have messages, that means we need
                        // to regenerate it. Return NOW
@@ -458,9 +466,9 @@ abstract class ResourceLoaderModule {
                // 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 );
+                       $this->contents[$contextHash] = $this->buildContent( $context );
                }
-               return $this->contents[ $contextHash ];
+               return $this->contents[$contextHash];
        }
 
        /**
@@ -472,6 +480,8 @@ abstract class ResourceLoaderModule {
         */
        final protected function buildContent( ResourceLoaderContext $context ) {
                $rl = $context->getResourceLoader();
+               $stats = RequestContext::getMain()->getStats();
+               $statStart = microtime( true );
 
                // 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
@@ -527,7 +537,8 @@ abstract class ResourceLoaderModule {
                                                                $stylePairs[$media] = array();
                                                                foreach ( $style as $cssText ) {
                                                                        if ( is_string( $cssText ) ) {
-                                                                               $stylePairs[$media][] = $rl->filter( 'minify-css', $cssText );
+                                                                               $stylePairs[$media][] =
+                                                                                       $rl->filter( 'minify-css', $cssText );
                                                                        }
                                                                }
                                                        } elseif ( is_string( $style ) ) {
@@ -559,6 +570,11 @@ abstract class ResourceLoaderModule {
                        $content['templates'] = $templates;
                }
 
+               $statTiming = microtime( true ) - $statStart;
+               $statName = str_replace( '.', '_', $this->getName() );
+               $stats->timing( "resourceloader_build.all", $statTiming );
+               $stats->timing( "resourceloader_build.$statName", $statTiming );
+
                return $content;
        }
 
@@ -585,32 +601,61 @@ abstract class ResourceLoaderModule {
         * @return string Hash (should use ResourceLoader::makeHash)
         */
        public function getVersionHash( ResourceLoaderContext $context ) {
+               // The startup module produces a manifest with versions representing the entire module.
+               // Typically, the request for the startup module itself has only=scripts. That must apply
+               // only to the startup module content, and not to the module version computed here.
+               $context = new DerivativeResourceLoaderContext( $context );
+               $context->setModules( array() );
+               // Version hash must cover all resources, regardless of startup request itself.
+               $context->setOnly( null );
+               // Compute version hash based on content, not debug urls.
+               $context->setDebug( false );
+
                // Cache this somewhat expensive operation. Especially because some classes
                // (e.g. startup module) iterate more than once over all modules to get versions.
                $contextHash = $context->getHash();
                if ( !array_key_exists( $contextHash, $this->versionHash ) ) {
 
-                       $summary = $this->getDefinitionSummary( $context );
-                       if ( !isset( $summary['_cacheEpoch'] ) ) {
-                               throw new Exception( 'getDefinitionSummary must call parent method' );
-                       }
-                       $str = json_encode( $summary );
+                       if ( $this->enableModuleContentVersion() ) {
+                               // Detect changes directly
+                               $str = json_encode( $this->getModuleContent( $context ) );
+                       } else {
+                               // Infer changes based on definition and other metrics
+                               $summary = $this->getDefinitionSummary( $context );
+                               if ( !isset( $summary['_cacheEpoch'] ) ) {
+                                       throw new LogicException( 'getDefinitionSummary must call parent method' );
+                               }
+                               $str = json_encode( $summary );
 
-                       $mtime = $this->getModifiedTime( $context );
-                       if ( $mtime !== null ) {
-                               // Support: MediaWiki 1.25 and earlier
-                               $str .= strval( $mtime );
-                       }
+                               $mtime = $this->getModifiedTime( $context );
+                               if ( $mtime !== null ) {
+                                       // Support: MediaWiki 1.25 and earlier
+                                       $str .= strval( $mtime );
+                               }
 
-                       $mhash = $this->getModifiedHash( $context );
-                       if ( $mhash !== null ) {
-                               // Support: MediaWiki 1.25 and earlier
-                               $str .= strval( $mhash );
+                               $mhash = $this->getModifiedHash( $context );
+                               if ( $mhash !== null ) {
+                                       // Support: MediaWiki 1.25 and earlier
+                                       $str .= strval( $mhash );
+                               }
                        }
 
-                       $this->versionHash[ $contextHash ] = ResourceLoader::makeHash( $str );
+                       $this->versionHash[$contextHash] = ResourceLoader::makeHash( $str );
                }
-               return $this->versionHash[ $contextHash ];
+               return $this->versionHash[$contextHash];
+       }
+
+       /**
+        * Whether to generate version hash based on module content.
+        *
+        * If a module requires database or file system access to build the module
+        * content, consider disabling this in favour of manually tracking relevant
+        * aspects in getDefinitionSummary(). See getVersionHash() for how this is used.
+        *
+        * @return bool
+        */
+       public function enableModuleContentVersion() {
+               return false;
        }
 
        /**
@@ -748,8 +793,13 @@ abstract class ResourceLoaderModule {
        protected function validateScriptFile( $fileName, $contents ) {
                if ( $this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) {
                        // Try for cache hit
-                       // Use CACHE_ANYTHING since filtering is very slow compared to DB queries
-                       $key = wfMemcKey( 'resourceloader', 'jsparse', self::$parseCacheVersion, md5( $contents ) );
+                       // Use CACHE_ANYTHING since parsing JS is much slower than a DB query
+                       $key = wfMemcKey(
+                               'resourceloader',
+                               'jsparse',
+                               self::$parseCacheVersion,
+                               md5( $contents )
+                       );
                        $cache = wfGetCache( CACHE_ANYTHING );
                        $cacheEntry = $cache->get( $key );
                        if ( is_string( $cacheEntry ) ) {
@@ -763,7 +813,8 @@ abstract class ResourceLoaderModule {
                        } 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" ) . ");";
+                               $result = "mw.log.error(" .
+                                       Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");";
                        }
 
                        $cache->set( $key, $result );