diff options
Diffstat (limited to 'includes/resourceloader/ResourceLoaderModule.php')
-rw-r--r-- | includes/resourceloader/ResourceLoaderModule.php | 156 |
1 files changed, 111 insertions, 45 deletions
diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 9c49c45f..11264fc8 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -58,6 +58,7 @@ abstract class ResourceLoaderModule { /* Protected Members */ protected $name = null; + protected $targets = array( 'desktop' ); // In-object cache for file dependencies protected $fileDeps = array(); @@ -70,17 +71,17 @@ abstract class ResourceLoaderModule { * Get this module's name. This is set when the module is registered * with ResourceLoader::register() * - * @return Mixed: Name (string) or null if no name was set + * @return mixed: Name (string) or null if no name was set */ public function getName() { return $this->name; } /** - * Set this module's name. This is called by ResourceLodaer::register() + * Set this module's name. This is called by ResourceLoader::register() * when registering the module. Other code should not call this. * - * @param $name String: Name + * @param string $name Name */ public function setName( $name ) { $this->name = $name; @@ -90,25 +91,25 @@ abstract class ResourceLoaderModule { * Get this module's origin. This is set when the module is registered * with ResourceLoader::register() * - * @return Int ResourceLoaderModule class constant, the subclass default - * if not set manuall + * @return int: ResourceLoaderModule class constant, the subclass default + * if not set manually */ public function getOrigin() { return $this->origin; } /** - * Set this module's origin. This is called by ResourceLodaer::register() + * Set this module's origin. This is called by ResourceLoader::register() * when registering the module. Other code should not call this. * - * @param $origin Int origin + * @param int $origin origin */ public function setOrigin( $origin ) { $this->origin = $origin; } /** - * @param $context ResourceLoaderContext + * @param ResourceLoaderContext $context * @return bool */ public function getFlip( $context ) { @@ -121,8 +122,8 @@ abstract class ResourceLoaderModule { * Get all JS for this module for a given language and skin. * Includes all relevant JS except loader scripts. * - * @param $context ResourceLoaderContext: Context object - * @return String: JavaScript code + * @param ResourceLoaderContext $context + * @return string: JavaScript code */ public function getScript( ResourceLoaderContext $context ) { // Stub, override expected @@ -140,8 +141,8 @@ abstract class ResourceLoaderModule { * #2 is important to prevent an infinite loop, therefore this function * MUST return either an only= URL or a non-load.php URL. * - * @param $context ResourceLoaderContext: Context object - * @return Array of URLs + * @param ResourceLoaderContext $context + * @return array: Array of URLs */ public function getScriptURLsForDebug( ResourceLoaderContext $context ) { $url = ResourceLoader::makeLoaderURL( @@ -171,8 +172,8 @@ abstract class ResourceLoaderModule { /** * Get all CSS for this module for a given skin. * - * @param $context ResourceLoaderContext: Context object - * @return Array: List of CSS strings or array of CSS strings keyed by media type. + * @param ResourceLoaderContext $context + * @return array: List of CSS strings or array of CSS strings keyed by media type. * like array( 'screen' => '.foo { width: 0 }' ); * or array( 'screen' => array( '.foo { width: 0 }' ) ); */ @@ -187,8 +188,8 @@ abstract class ResourceLoaderModule { * the module, but file-based modules will want to override this to * load the files directly. See also getScriptURLsForDebug() * - * @param $context ResourceLoaderContext: Context object - * @return Array: array( mediaType => array( URL1, URL2, ... ), ... ) + * @param ResourceLoaderContext $context + * @return array: array( mediaType => array( URL1, URL2, ... ), ... ) */ public function getStyleURLsForDebug( ResourceLoaderContext $context ) { $url = ResourceLoader::makeLoaderURL( @@ -210,7 +211,7 @@ abstract class ResourceLoaderModule { * * To get a JSON blob with messages, use MessageBlobStore::get() * - * @return Array: List of message keys. Keys may occur more than once + * @return array: List of message keys. Keys may occur more than once */ public function getMessages() { // Stub, override expected @@ -220,7 +221,7 @@ abstract class ResourceLoaderModule { /** * Get the group this module is in. * - * @return String: Group name + * @return string: Group name */ public function getGroup() { // Stub, override expected @@ -230,7 +231,7 @@ abstract class ResourceLoaderModule { /** * Get the origin of this module. Should only be overridden for foreign modules. * - * @return String: Origin name, 'local' for local modules + * @return string: Origin name, 'local' for local modules */ public function getSource() { // Stub, override expected @@ -262,7 +263,7 @@ abstract class ResourceLoaderModule { /** * Get the loader JS for this module, if set. * - * @return Mixed: JavaScript loader code as a string or boolean false if no custom loader set + * @return mixed: JavaScript loader code as a string or boolean false if no custom loader set */ public function getLoaderScript() { // Stub, override expected @@ -273,16 +274,11 @@ abstract class ResourceLoaderModule { * Get a list of modules this module depends on. * * Dependency information is taken into account when loading a module - * on the client side. When adding a module on the server side, - * dependency information is NOT taken into account and YOU are - * responsible for adding dependent modules as well. If you don't do - * this, the client side loader will send a second request back to the - * server to fetch the missing modules, which kind of defeats the - * purpose of the resource loader. + * on the client side. * * To add dependencies dynamically on the client side, use a custom * loader script, see getLoaderScript() - * @return Array: List of module names as strings + * @return array: List of module names as strings */ public function getDependencies() { // Stub, override expected @@ -290,11 +286,20 @@ abstract class ResourceLoaderModule { } /** + * Get target(s) for the module, eg ['desktop'] or ['desktop', 'mobile'] + * + * @return array: Array of strings + */ + public function getTargets() { + return $this->targets; + } + + /** * 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 $skin String: Skin name - * @return Array: List of files + * @param string $skin Skin name + * @return array: List of files */ public function getFileDependencies( $skin ) { // Try in-object cache first @@ -309,7 +314,7 @@ abstract class ResourceLoaderModule { ), __METHOD__ ); if ( !is_null( $deps ) ) { - $this->fileDeps[$skin] = (array) FormatJson::decode( $deps, true ); + $this->fileDeps[$skin] = (array)FormatJson::decode( $deps, true ); } else { $this->fileDeps[$skin] = array(); } @@ -319,8 +324,8 @@ abstract class ResourceLoaderModule { /** * Set preloaded file dependency information. Used so we can load this * information for all modules at once. - * @param $skin String: Skin name - * @param $deps Array: Array of file names + * @param string $skin Skin name + * @param array $deps Array of file names */ public function setFileDependencies( $skin, $deps ) { $this->fileDeps[$skin] = $deps; @@ -329,13 +334,14 @@ abstract class ResourceLoaderModule { /** * Get the last modification timestamp of the message blob for this * module in a given language. - * @param $lang String: Language code - * @return Integer: UNIX timestamp, or 0 if the module doesn't have messages + * @param string $lang Language code + * @return int: UNIX timestamp, or 0 if the module doesn't have messages */ public function getMsgBlobMtime( $lang ) { if ( !isset( $this->msgBlobMtime[$lang] ) ) { - if ( !count( $this->getMessages() ) ) + if ( !count( $this->getMessages() ) ) { return 0; + } $dbr = wfGetDB( DB_SLAVE ); $msgBlobMtime = $dbr->selectField( 'msg_resource', 'mr_timestamp', array( @@ -356,7 +362,7 @@ abstract class ResourceLoaderModule { /** * Set a preloaded message blob last modification timestamp. Used so we * can load this information for all modules at once. - * @param $lang String: Language code + * @param string $lang Language code * @param $mtime Integer: UNIX timestamp or 0 if there is no such blob */ public function setMsgBlobMtime( $lang, $mtime ) { @@ -375,9 +381,13 @@ abstract class ResourceLoaderModule { * NOTE: The mtime of the module's messages is NOT automatically included. * If you want this to happen, you'll need to call getMsgBlobMtime() * yourself and take its result into consideration. - * - * @param $context ResourceLoaderContext: Context object - * @return Integer: 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 @@ -385,19 +395,59 @@ abstract class ResourceLoaderModule { } /** + * Helper method for calculating when the module's hash (if it has one) changed. + * + * @param ResourceLoaderContext $context + * @return integer: UNIX timestamp or 0 if there is no hash provided + */ + 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() ); + + $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 last modification timestamp of the message blob for this + * module in a given language. + * + * @param ResourceLoaderContext $context + * @return string|null: Hash + */ + public function getModifiedHash( ResourceLoaderContext $context ) { + return null; + } + + /** * 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 * definitely going to be empty, it should override this method to * return true in that case. Callers may optimize the request for this * module away if this function returns true. - * @param $context ResourceLoaderContext: Context object - * @return Boolean + * @param ResourceLoaderContext $context + * @return bool */ public function isKnownEmpty( ResourceLoaderContext $context ) { return false; } - /** @var JSParser lazy-initialized; use self::javaScriptParser() */ private static $jsParser; private static $parseCacheVersion = 1; @@ -408,7 +458,7 @@ abstract class ResourceLoaderModule { * * @param string $fileName * @param string $contents - * @return string JS with the original, or a replacement error + * @return string: JS with the original, or a replacement error */ protected function validateScriptFile( $fileName, $contents ) { global $wgResourceLoaderValidateJS; @@ -426,10 +476,10 @@ abstract class ResourceLoaderModule { try { $parser->parse( $contents, $fileName, 1 ); $result = $contents; - } catch (Exception $e) { + } catch ( Exception $e ) { // We'll save this to cache to avoid having to validate broken JS over and over... $err = $e->getMessage(); - $result = "throw new Error(" . Xml::encodeJsVar("JavaScript parse error: $err") . ");"; + $result = "throw new Error(" . Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");"; } $cache->set( $key, $result ); @@ -449,4 +499,20 @@ abstract class ResourceLoaderModule { return self::$jsParser; } + /** + * Safe version of filemtime(), which doesn't throw a PHP warning if the file doesn't exist + * but returns 1 instead. + * @param string $filename File name + * @return int UNIX timestamp, or 1 if the file doesn't exist + */ + protected static function safeFilemtime( $filename ) { + if ( file_exists( $filename ) ) { + return filemtime( $filename ); + } else { + // We only ever map this function on an array if we're gonna call max() after, + // so return our standard minimum timestamps here. This is 1, not 0, because + // wfTimestamp(0) == NOW + return 1; + } + } } |