/**
* Get the files this module depends on indirectly for a given skin.
- * Currently these are only image files referenced by the module's CSS.
*
- * @param string $skin Skin name
+ * These are only image files referenced by the module's stylesheet.
+ *
+ * @param ResourceLoaderContext $context
* @return array List of files
*/
- public function getFileDependencies( $skin ) {
+ protected function getFileDependencies( ResourceLoaderContext $context ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
+
// Try in-object cache first
- if ( isset( $this->fileDeps[$skin] ) ) {
- return $this->fileDeps[$skin];
+ if ( !isset( $this->fileDeps[$vary] ) ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $deps = $dbr->selectField( 'module_deps',
+ 'md_deps',
+ array(
+ 'md_module' => $this->getName(),
+ 'md_skin' => $vary,
+ ),
+ __METHOD__
+ );
+
+ if ( !is_null( $deps ) ) {
+ $this->fileDeps[$vary] = self::expandRelativePaths(
+ (array)FormatJson::decode( $deps, true )
+ );
+ } else {
+ $this->fileDeps[$vary] = array();
+ }
}
+ return $this->fileDeps[$vary];
+ }
- $dbr = wfGetDB( DB_SLAVE );
- $deps = $dbr->selectField( 'module_deps',
- 'md_deps',
- array(
- 'md_module' => $this->getName(),
- 'md_skin' => $skin,
- ),
- __METHOD__
- );
+ /**
+ * Set in-object cache for file dependencies.
+ *
+ * This is used to retrieve data in batches. See ResourceLoader::preloadModuleInfo().
+ * To save the data, use saveFileDependencies().
+ *
+ * @param string $skin Skin name
+ * @param array $deps Array of file names
+ */
+ public function setFileDependencies( ResourceLoaderContext $context, $files ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
+ $this->fileDeps[$vary] = $files;
+ }
- if ( !is_null( $deps ) ) {
- $this->fileDeps[$skin] = (array)FormatJson::decode( $deps, true );
- } else {
- $this->fileDeps[$skin] = array();
+ /**
+ * Set the files this module depends on indirectly for a given skin.
+ *
+ * @since 1.26
+ * @param ResourceLoaderContext $context
+ * @param array $localFileRefs List of files
+ */
+ protected function saveFileDependencies( ResourceLoaderContext $context, $localFileRefs ) {
+ // Normalise array
+ $localFileRefs = array_values( array_unique( $localFileRefs ) );
+ sort( $localFileRefs );
+
+ try {
+ // If the list has been modified since last time we cached it, update the cache
+ if ( $localFileRefs !== $this->getFileDependencies( $context ) ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->replace( 'module_deps',
+ array( array( 'md_module', 'md_skin' ) ), array(
+ '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 ) ),
+ )
+ );
+ }
+ } catch ( Exception $e ) {
+ wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
}
+ }
- return $this->fileDeps[$skin];
+ /**
+ * Make file paths relative to MediaWiki directory.
+ *
+ * This is used to make file paths safe for storing in a database without the paths
+ * becoming stale or incorrect when MediaWiki is moved or upgraded (T111481).
+ *
+ * @since 1.26
+ * @param array $filePaths
+ * @return array
+ */
+ protected static function getRelativePaths( Array $filePaths ) {
+ global $IP;
+ return array_map( function ( $path ) use ( $IP ) {
+ return RelPath\getRelativePath( $path, $IP );
+ }, $filePaths );
}
/**
- * Set preloaded file dependency information. Used so we can load this
- * information for all modules at once.
- * @param string $skin Skin name
- * @param array $deps Array of file names
+ * Expand directories relative to $IP.
+ *
+ * @since 1.26
+ * @param array $filePaths
+ * @return array
*/
- public function setFileDependencies( $skin, $deps ) {
- $this->fileDeps[$skin] = $deps;
+ protected static function expandRelativePaths( Array $filePaths ) {
+ global $IP;
+ return array_map( function ( $path ) use ( $IP ) {
+ return RelPath\joinPath( $IP, $path );
+ }, $filePaths );
}
/**
}
/**
- * Set a preloaded message blob last modification timestamp. Used so we
- * can load this information for all modules at once.
+ * Set in-object cache for message blob time.
+ *
+ * This is used to retrieve data in batches. See ResourceLoader::preloadModuleInfo().
+ *
* @param string $lang Language code
* @param int $mtime UNIX timestamp
*/
$this->msgBlobMtime[$lang] = $mtime;
}
+ /**
+ * Get module-specific LESS variables, if any.
+ *
+ * @since 1.26
+ * @param ResourceLoaderContext $context
+ * @return array Module-specific LESS variables.
+ */
+ protected function getLessVars( ResourceLoaderContext $context ) {
+ return array();
+ }
+
/**
* Get an array of this module's resources. Ready for serving to the web.
*
* @return string Hash
*/
protected static function safeFileHash( $filePath ) {
- MediaWiki\suppressWarnings();
- $contents = file_get_contents( $filePath );
- MediaWiki\restoreWarnings();
- if ( $contents !== false ) {
- $hash = hash( 'md4', $contents );
- } else {
- $hash = '';
- }
- return $hash;
+ return FileContentsHasher::getFileContentsHash( $filePath );
}
}