* If you want this to happen, you'll need to call getMsgBlobMtime()
* yourself and take its result into consideration.
*
- * @param ResourceLoaderContext $context
- * @return int: UNIX timestamp
+ * NOTE: The mtime of the module's hash is NOT automatically included.
+ * If your module provides a getModifiedHash() method, you'll need to call getHashMtime()
+ * yourself and take its result into consideration.
+ *
+ * @param ResourceLoaderContext $context Context object
+ * @return integer UNIX timestamp
*/
public function getModifiedTime( ResourceLoaderContext $context ) {
// 0 would mean now
return 1;
}
+ /**
+ * Helper method for calculating when the module's hash (if it has one) changed.
+ *
+ * @param ResourceLoaderContext $context
+ * @return integer: UNIX timestamp or 0 if no hash was provided
+ * by getModifiedHash()
+ */
+ public function getHashMtime( ResourceLoaderContext $context ) {
+ $hash = $this->getModifiedHash( $context );
+ if ( !is_string( $hash ) ) {
+ return 0;
+ }
+
+ $cache = wfGetCache( CACHE_ANYTHING );
+ $key = wfMemcKey( 'resourceloader', 'modulemodifiedhash', $this->getName(), $hash );
+
+ $data = $cache->get( $key );
+ if ( is_array( $data ) && $data['hash'] === $hash ) {
+ // Hash is still the same, re-use the timestamp of when we first saw this hash.
+ return $data['timestamp'];
+ }
+
+ $timestamp = wfTimestamp();
+ $cache->set( $key, array(
+ 'hash' => $hash,
+ 'timestamp' => $timestamp,
+ ) );
+
+ return $timestamp;
+ }
+
+ /**
+ * Get the hash for whatever this module may contain.
+ *
+ * This is the method subclasses should implement if they want to make
+ * use of getHashMTime() inside getModifiedTime().
+ *
+ * @param ResourceLoaderContext $context
+ * @return string|null: Hash
+ */
+ public function getModifiedHash( ResourceLoaderContext $context ) {
+ return null;
+ }
+
+ /**
+ * Helper method for calculating when this module's definition summary was last changed.
+ *
+ * @return integer: UNIX timestamp or 0 if no definition summary was provided
+ * by getDefinitionSummary()
+ */
+ public function getDefinitionMtime( ResourceLoaderContext $context ) {
+ wfProfileIn( __METHOD__ );
+ $summary = $this->getDefinitionSummary( $context );
+ if ( $summary === null ) {
+ wfProfileOut( __METHOD__ );
+ return 0;
+ }
+
+ $hash = md5( json_encode( $summary ) );
+
+ $cache = wfGetCache( CACHE_ANYTHING );
+
+ // Embed the hash itself in the cache key. This allows for a few nifty things:
+ // - During deployment, servers with old and new versions of the code communicating
+ // with the same memcached will not override the same key repeatedly increasing
+ // the timestamp.
+ // - In case of the definition changing and then changing back in a short period of time
+ // (e.g. in case of a revert or a corrupt server) the old timestamp and client-side cache
+ // url will be re-used.
+ // - If different context-combinations (e.g. same skin, same language or some combination
+ // thereof) result in the same definition, they will use the same hash and timestamp.
+ $key = wfMemcKey( 'resourceloader', 'moduledefinition', $this->getName(), $hash );
+
+ $data = $cache->get( $key );
+ if ( is_int( $data ) && $data > 0 ) {
+ // We've seen this hash before, re-use the timestamp of when we first saw it.
+ wfProfileOut( __METHOD__ );
+ return $data;
+ }
+
+ wfDebugLog( 'resourceloader', __METHOD__ . ": New definition hash for module {$this->getName()} in context {$context->getHash()}: $hash." );
+
+ $timestamp = time();
+ $cache->set( $key, $timestamp );
+
+ wfProfileOut( __METHOD__ );
+ return $timestamp;
+ }
+
+ /**
+ * Get the definition summary for this module.
+ *
+ * This is the method subclasses should implement if they want to make
+ * use of getDefinitionMTime() inside getModifiedTime().
+ *
+ * Return an array containing values from all significant properties of this
+ * module's definition. Be sure to include things that are explicitly ordered,
+ * in their actaul order (bug 37812).
+ *
+ * Avoid including things that are insiginificant (e.g. order of message
+ * keys is insignificant and should be sorted to avoid unnecessary cache
+ * invalidation).
+ *
+ * Avoid including things already considered by other methods inside your
+ * getModifiedTime(), such as file mtime timestamps.
+ *
+ * Serialisation is done using json_encode, which means object state is not
+ * taken into account when building the hash. This data structure must only
+ * contain arrays and scalars as values (avoid object instances) which means
+ * it requires abstraction.
+ *
+ * @return Array|null
+ */
+ public function getDefinitionSummary( ResourceLoaderContext $context ) {
+ return array(
+ 'class' => get_class( $this ),
+ );
+ }
+
/**
* Check whether this module is known to be empty. If a child class
* has an easy and cheap way to determine that this module is
private static $jsParser;
private static $parseCacheVersion = 1;
- /** @var array Global LESS variables */
- private static $lessVars;
-
/**
* Validate a given script file; if valid returns the original source.
* If invalid, returns replacement JS source that throws an exception.
return self::$jsParser;
}
- /**
- * @since 1.22
- * @return lessc
- */
- protected static function lessCompiler() {
- global $wgResourceLoaderLESSFunctions, $wgResourceLoaderLESSImportPaths;
-
- $less = new lessc();
- $less->setPreserveComments( true );
- $less->setVariables( self::getLESSVars() );
- $less->setImportDir( $wgResourceLoaderLESSImportPaths );
- foreach ( $wgResourceLoaderLESSFunctions as $name => $func ) {
- $less->registerFunction( $name, $func );
- }
- return $less;
- }
-
- /**
- * Get global LESS variables.
- *
- * @since 1.22
- * @return array: Map of variable names to string CSS values.
- */
- protected static function getLESSVars() {
- global $wgResourceLoaderLESSVars;
-
- if ( self::$lessVars === null ) {
- self::$lessVars = $wgResourceLoaderLESSVars;
- // Sort by key to ensure consistent hashing for cache lookups.
- ksort( self::$lessVars );
- }
- return self::$lessVars;
- }
-
/**
* Safe version of filemtime(), which doesn't throw a PHP warning if the file doesn't exist
* but returns 1 instead.