class MessageBlobStore {
/**
* Get the message blobs for a set of modules
- * @param $modules array Array of module names
+ * @param $modules array Array of module objects keyed by module name
* @param $lang string Language code
* @return array An array mapping module names to message blobs
*/
return array();
}
// Try getting from the DB first
- $blobs = self::getFromDB( $modules, $lang );
+ $blobs = self::getFromDB( array_keys( $modules ), $lang );
// Generate blobs for any missing modules and store them in the DB
- $missing = array_diff( $modules, array_keys( $blobs ) );
- foreach ( $missing as $module ) {
- $blob = self::insertMessageBlob( $module, $lang );
+ $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) );
+ foreach ( $missing as $name ) {
+ $blob = self::insertMessageBlob( $name, $modules[$name], $lang );
if ( $blob ) {
- $blobs[$module] = $blob;
+ $blobs[$name] = $blob;
}
}
* @param $lang string Language code
* @return mixed Message blob or false if the module has no messages
*/
- public static function insertMessageBlob( $module, $lang ) {
- $blob = self::generateMessageBlob( $module, $lang );
+ public static function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
+ $blob = self::generateMessageBlob( $name, $module, $lang );
if ( !$blob ) {
return false;
$dbw = wfGetDB( DB_MASTER );
$success = $dbw->insert( 'msg_resource', array(
'mr_lang' => $lang,
- 'mr_resource' => $module,
+ 'mr_resource' => $name,
'mr_blob' => $blob,
'mr_timestamp' => $dbw->timestamp()
),
if ( $dbw->affectedRows() == 0 ) {
// Blob was already present, fetch it
$blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
- 'mr_resource' => $module,
+ 'mr_resource' => $name,
'mr_lang' => $lang,
),
__METHOD__
} else {
// Update msg_resource_links
$rows = array();
- $mod = ResourceLoader::getModule( $module );
- foreach ( $mod->getMessages() as $key ) {
+ foreach ( $module->getMessages() as $key ) {
$rows[] = array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $key
);
}
* @return mixed If $lang is set, the new message blob for that language is
* returned if present. Otherwise, null is returned.
*/
- public static function updateModule( $module, $lang = null ) {
+ public static function updateModule( $name, ResourceLoaderModule $module, $lang = null ) {
$retval = null;
// Find all existing blobs for this module
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->select( 'msg_resource',
array( 'mr_lang', 'mr_blob' ),
- array( 'mr_resource' => $module ),
+ array( 'mr_resource' => $name ),
__METHOD__
);
foreach ( $res as $row ) {
$oldBlob = $row->mr_blob;
- $newBlob = self::generateMessageBlob( $module, $row->mr_lang );
+ $newBlob = self::generateMessageBlob( $name, $module, $row->mr_lang );
if ( $row->mr_lang === $lang ) {
$retval = $newBlob;
}
$newRows[] = array(
- 'mr_resource' => $module,
+ 'mr_resource' => $name,
'mr_lang' => $row->mr_lang,
'mr_blob' => $newBlob,
'mr_timestamp' => $now
// Delete removed messages, insert added ones
if ( $removed ) {
$dbw->delete( 'msg_resource_links', array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $removed
), __METHOD__
);
foreach ( $added as $message ) {
$newLinksRows[] = array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $message
);
}
* @param $lang string Language code
* @return string JSON object
*/
- private static function generateMessageBlob( $module, $lang ) {
- $mod = ResourceLoader::getModule( $module );
+ private static function generateMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
$messages = array();
- foreach ( $mod->getMessages() as $key ) {
+ foreach ( $module->getMessages() as $key ) {
$messages[$key] = wfMsgExt( $key, array( 'language' => $lang ) );
}
var $mScripts = '', $mLinkColours, $mPageLinkTitle = '', $mHeadItems = array();
var $mModules = array(), $mModuleScripts = array(), $mModuleStyles = array(), $mModuleMessages = array();
+ var $mResourceLoader;
var $mInlineMsg = array();
var $mTemplateIds = array();
}
// TODO: Document
- static function makeResourceLoaderLink( $skin, $modules, $only, $useESI = false ) {
+ protected function makeResourceLoaderLink( $skin, $modules, $only, $useESI = false ) {
global $wgUser, $wgLang, $wgRequest, $wgLoadScript, $wgResourceLoaderDebug, $wgResourceLoaderUseESI,
$wgResourceLoaderInlinePrivateModules;
+ // Lazy-load ResourceLoader
+ if ( is_null( $this->mResourceLoader ) ) {
+ $this->mResourceLoader = new ResourceLoader();
+ }
// TODO: Should this be a static function of ResourceLoader instead?
// TODO: Divide off modules starting with "user", and add the user parameter to them
$query = array(
// Create keyed-by-group list of module objects from modules list
$groups = array();
foreach ( (array) $modules as $name ) {
- $module = ResourceLoader::getModule( $name );
+ $module = $this->mResourceLoader->getModule( $name );
$group = $module->getGroup();
if ( !isset( $groups[$group] ) ) {
$groups[$group] = array();
}
// Support inlining of private modules if configured as such
if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
- $context = new ResourceLoaderContext( new FauxRequest( $query ) );
+ $context = new ResourceLoaderContext( $this->mResourceLoader, new FauxRequest( $query ) );
if ( $only == 'styles' ) {
$links .= Html::inlineStyle(
ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeModuleResponse( $context, $modules )
+ $this->mResourceLoader->makeModuleResponse( $context, $modules )
)
);
} else {
$links .= Html::inlineScript(
ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeModuleResponse( $context, $modules )
+ $this->mResourceLoader->makeModuleResponse( $context, $modules )
)
);
}
// we can ensure cache misses on change
if ( $group === 'user' || $group === 'site' ) {
// Create a fake request based on the one we are about to make so modules return correct times
- $context = new ResourceLoaderContext( new FauxRequest( $query ) );
+ $context = new ResourceLoaderContext( $this->mResourceLoader, new FauxRequest( $query ) );
// Get the maximum timestamp
$timestamp = 0;
foreach ( $modules as $module ) {
global $wgUser, $wgRequest, $wgUseSiteJs, $wgResourceLoaderDebug;
// Startup - this will immediately load jquery and mediawiki modules
- $scripts = self::makeResourceLoaderLink( $sk, 'startup', 'scripts', true );
+ $scripts = $this->makeResourceLoaderLink( $sk, 'startup', 'scripts', true );
// Configuration -- This could be merged together with the load and go, but makeGlobalVariablesScript returns a
// whole script tag -- grumble grumble...
if ( $wgRequest->getFuzzyBool( 'debug', $wgResourceLoaderDebug ) ) {
// Scripts
foreach ( $this->getModuleScripts() as $name ) {
- $scripts .= self::makeResourceLoaderLink( $sk, $name, 'scripts' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $name, 'scripts' );
}
// Messages
foreach ( $this->getModuleMessages() as $name ) {
- $scripts .= self::makeResourceLoaderLink( $sk, $name, 'messages' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $name, 'messages' );
}
} else {
// Scripts
if ( count( $this->getModuleScripts() ) ) {
- $scripts .= self::makeResourceLoaderLink( $sk, $this->getModuleScripts(), 'scripts' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts(), 'scripts' );
}
// Messages
if ( count( $this->getModuleMessages() ) ) {
- $scripts .= self::makeResourceLoaderLink( $sk, $this->getModuleMessages(), 'messages' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages(), 'messages' );
}
}
# XXX: additional security check/prompt?
$this->addInlineScript( $wgRequest->getText( 'wpTextbox1' ) );
} else {
- $scripts .= self::makeResourceLoaderLink( $sk, array( 'user', 'user.options' ), 'scripts' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, array( 'user', 'user.options' ), 'scripts' );
$userOptionsAdded = true;
}
}
if ( !$userOptionsAdded ) {
- $scripts .= self::makeResourceLoaderLink( $sk, 'user.options', 'scripts' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, 'user.options', 'scripts' );
}
$scripts .= "\n" . $this->mScripts;
// Add site JS if enabled
if ( $wgUseSiteJs ) {
- $scripts .= self::makeResourceLoaderLink( $sk, 'site', 'scripts' );
+ $scripts .= $this->makeResourceLoaderLink( $sk, 'site', 'scripts' );
}
return $scripts;
// Support individual script requests in debug mode
if ( $wgRequest->getFuzzyBool( 'debug', $wgResourceLoaderDebug ) ) {
foreach ( $this->getModuleStyles() as $name ) {
- $tags[] = self::makeResourceLoaderLink( $sk, $name, 'styles' );
+ $tags[] = $this->makeResourceLoaderLink( $sk, $name, 'styles' );
}
} else {
if ( count( $this->getModuleStyles() ) ) {
- $tags[] = self::makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles' );
+ $tags[] = $this->makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles' );
}
}
/* Protected Static Members */
// @var array list of module name/ResourceLoaderModule object pairs
- protected static $modules = array();
- protected static $initialized = false;
+ protected $modules = array();
- /* Protected Static Methods */
-
- /**
- * Registers core modules and runs registration hooks
- */
- protected static function initialize() {
- global $IP;
-
- // Safety check - this should never be called more than once
- if ( !self::$initialized ) {
- wfProfileIn( __METHOD__ );
- // This needs to be first, because hooks might call ResourceLoader
- // public interfaces which will call this
- self::$initialized = true;
- self::register( include( "$IP/resources/Resources.php" ) );
- wfRunHooks( 'ResourceLoaderRegisterModules' );
- wfProfileOut( __METHOD__ );
- }
- }
+ /* Protected Methods */
- /*
+ /**
* Loads information stored in the database about modules
*
* This is not inside the module code because it's so much more performant to request all of the information at once
* @param $modules array list of module names to preload information for
* @param $context ResourceLoaderContext context to load the information within
*/
- protected static function preloadModuleInfo( array $modules, ResourceLoaderContext $context ) {
+ protected function preloadModuleInfo( array $modules, ResourceLoaderContext $context ) {
$dbr = wfGetDb( DB_SLAVE );
$skin = $context->getSkin();
$lang = $context->getLanguage();
$modulesWithDeps = array();
foreach ( $res as $row ) {
- self::$modules[$row->md_module]->setFileDependencies( $skin,
+ $this->modules[$row->md_module]->setFileDependencies( $skin,
FormatJson::decode( $row->md_deps, true )
);
$modulesWithDeps[] = $row->md_module;
}
// Register the absence of a dependencies row too
foreach ( array_diff( $modules, $modulesWithDeps ) as $name ) {
- self::$modules[$name]->setFileDependencies( $skin, array() );
+ $this->modules[$name]->setFileDependencies( $skin, array() );
}
// Get message blob mtimes. Only do this for modules with messages
$modulesWithMessages = array();
$modulesWithoutMessages = array();
foreach ( $modules as $name ) {
- if ( count( self::$modules[$name]->getMessages() ) ) {
+ if ( count( $this->modules[$name]->getMessages() ) ) {
$modulesWithMessages[] = $name;
} else {
$modulesWithoutMessages[] = $name;
), __METHOD__
);
foreach ( $res as $row ) {
- self::$modules[$row->mr_resource]->setMsgBlobMtime( $lang, $row->mr_timestamp );
+ $this->modules[$row->mr_resource]->setMsgBlobMtime( $lang, $row->mr_timestamp );
}
}
foreach ( $modulesWithoutMessages as $name ) {
- self::$modules[$name]->setMsgBlobMtime( $lang, 0 );
+ $this->modules[$name]->setMsgBlobMtime( $lang, 0 );
}
}
* @param $file String: path to file being filtered, (optional: only required for CSS to resolve paths)
* @return String: filtered data
*/
- protected static function filter( $filter, $data ) {
+ protected function filter( $filter, $data ) {
global $wgMemc;
wfProfileIn( __METHOD__ );
return $result;
}
- /* Static Methods */
+ /* Methods */
+
+ /**
+ * Registers core modules and runs registration hooks
+ */
+ public function __construct() {
+ global $IP;
+
+ wfProfileIn( __METHOD__ );
+
+ // Register core modules
+ $this->register( include( "$IP/resources/Resources.php" ) );
+ // Register extension modules
+ wfRunHooks( 'ResourceLoaderRegisterModules', array( &$this ) );
+
+ wfProfileOut( __METHOD__ );
+ }
/**
* Registers a module with the ResourceLoader system.
* happened, but in bringing errors to the client in a way that they can
* easily see them if they want to, such as by using FireBug
*/
- public static function register( $name, ResourceLoaderModule $object = null ) {
+ public function register( $name, ResourceLoaderModule $object = null ) {
wfProfileIn( __METHOD__ );
- self::initialize();
// Allow multiple modules to be registered in one call
if ( is_array( $name ) && !isset( $object ) ) {
foreach ( $name as $key => $value ) {
- self::register( $key, $value );
+ $this->register( $key, $value );
}
wfProfileOut( __METHOD__ );
}
// Disallow duplicate registrations
- if ( isset( self::$modules[$name] ) ) {
+ if ( isset( $this->modules[$name] ) ) {
// A module has already been registered by this name
throw new MWException( 'Another module has already been registered as ' . $name );
}
// Attach module
- self::$modules[$name] = $object;
+ $this->modules[$name] = $object;
$object->setName( $name );
+
wfProfileOut( __METHOD__ );
}
*
* @return Array: array( modulename => ResourceLoaderModule )
*/
- public static function getModules() {
-
- self::initialize();
-
- return self::$modules;
+ public function getModules() {
+ return $this->modules;
}
/**
* @param $name String: module name
* @return mixed ResourceLoaderModule or null if not registered
*/
- public static function getModule( $name ) {
-
- self::initialize();
-
- return isset( self::$modules[$name] ) ? self::$modules[$name] : null;
- }
-
- /**
- * Get the highest modification time of all modules, based on a given
- * combination of language code, skin name and debug mode flag.
- *
- * @param $context ResourceLoaderContext object
- * @return Integer: UNIX timestamp
- */
- public static function getHighestModifiedTime( ResourceLoaderContext $context ) {
-
- self::initialize();
-
- $time = 1; // wfTimestamp() treats 0 as 'now', so that's not a suitable choice
-
- foreach ( self::$modules as $module ) {
- $time = max( $time, $module->getModifiedTime( $context ) );
- }
-
- return $time;
+ public function getModule( $name ) {
+ return isset( $this->modules[$name] ) ? $this->modules[$name] : null;
}
/**
*
* @param $context ResourceLoaderContext object
*/
- public static function respond( ResourceLoaderContext $context ) {
+ public function respond( ResourceLoaderContext $context ) {
global $wgResourceLoaderMaxage;
wfProfileIn( __METHOD__ );
- self::initialize();
// Split requested modules into two groups, modules and missing
$modules = array();
$missing = array();
foreach ( $context->getModules() as $name ) {
- if ( isset( self::$modules[$name] ) ) {
- $modules[$name] = self::$modules[$name];
+ if ( isset( $this->modules[$name] ) ) {
+ $modules[$name] = $this->modules[$name];
} else {
$missing[] = $name;
}
}
// Preload information needed to the mtime calculation below
- self::preloadModuleInfo( array_keys( $modules ), $context );
+ $this->preloadModuleInfo( array_keys( $modules ), $context );
// To send Last-Modified and support If-Modified-Since, we need to detect
// the last modified time
wfProfileOut( __METHOD__ );
}
- public static function makeModuleResponse( ResourceLoaderContext $context, array $modules, $missing = null ) {
+ public function makeModuleResponse( ResourceLoaderContext $context, array $modules, $missing = null ) {
// Pre-fetch blobs
$blobs = $context->shouldIncludeMessages() ?
- MessageBlobStore::get( array_keys( $modules ), $context->getLanguage() ) : array();
+ MessageBlobStore::get( $modules, $context->getLanguage() ) : array();
// Generate output
$out = '';
( count( $styles = $module->getStyles( $context ) ) )
) {
// Flip CSS on a per-module basis
- if ( self::$modules[$name]->getFlip( $context ) ) {
+ if ( $this->modules[$name]->getFlip( $context ) ) {
foreach ( $styles as $media => $style ) {
- $styles[$media] = self::filter( 'flip-css', $style );
+ $styles[$media] = $this->filter( 'flip-css', $style );
}
}
}
// Minify CSS before embedding in mediaWiki.loader.implement call (unless in debug mode)
if ( !$context->getDebug() ) {
foreach ( $styles as $media => $style ) {
- $styles[$media] = self::filter( 'minify-css', $style );
+ $styles[$media] = $this->filter( 'minify-css', $style );
}
}
$out .= self::makeLoaderImplementScript( $name, $scripts, $styles, $messages );
return $out;
} else {
if ( $context->getOnly() === 'styles' ) {
- return self::filter( 'minify-css', $out );
+ return $this->filter( 'minify-css', $out );
} else {
- return self::filter( 'minify-js', $out );
+ return $this->filter( 'minify-js', $out );
}
}
}
- // Client code generation methods
+ /* Static Methods */
public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) {
if ( is_array( $scripts ) ) {
/* Protected Members */
+ protected $resourceLoader;
protected $request;
protected $modules;
protected $language;
/* Methods */
- public function __construct( WebRequest $request ) {
+ public function __construct( ResourceLoader $resourceLoader, WebRequest $request ) {
global $wgLang, $wgDefaultSkin, $wgResourceLoaderDebug;
+ $this->resourceLoader = $resourceLoader;
$this->request = $request;
// Interperet request
$this->modules = explode( '|', $request->getVal( 'modules' ) );
}
}
+ public function getResourceLoader() {
+ return $this->resourceLoader;
+ }
+
public function getRequest() {
return $this->request;
}
$out = '';
$registrations = array();
- foreach ( ResourceLoader::getModules() as $name => $module ) {
+ foreach ( $context->getResourceLoader()->getModules() as $name => $module ) {
// Support module loader scripts
if ( ( $loader = $module->getLoaderScript() ) !== false ) {
$deps = FormatJson::encode( $module->getDependencies() );
'skin' => $context->getSkin(),
'debug' => $context->getDebug() ? 'true' : 'false',
'version' => wfTimestamp( TS_ISO_8601, round( max(
- ResourceLoader::getModule( 'jquery' )->getModifiedTime( $context ),
- ResourceLoader::getModule( 'mediawiki' )->getModifiedTime( $context )
+ $context->getResourceLoader()->getModule( 'jquery' )->getModifiedTime( $context ),
+ $context->getResourceLoader()->getModule( 'mediawiki' )->getModifiedTime( $context )
), -2 ) )
);
// Ensure uniform query order
return $this->modifiedTime[$hash];
}
$this->modifiedTime[$hash] = filemtime( "$IP/resources/startup.js" );
-
+
// ATTENTION!: Because of the line above, this is not going to cause infinite recursion - think carefully
// before making changes to this code!
- return $this->modifiedTime[$hash] = ResourceLoader::getHighestModifiedTime( $context );
+ $time = 1; // wfTimestamp() treats 0 as 'now', so that's not a suitable choice
+ foreach ( $context->getResourceLoader()->getModules() as $module ) {
+ $time = max( $time, $module->getModifiedTime( $context ) );
+ }
+ return $this->modifiedTime[$hash] = $time;
}
public function getFlip( $context ) {
}
// Respond to resource loading request
-ResourceLoader::respond( new ResourceLoaderContext( $wgRequest ) );
+$resourceLoader = new ResourceLoader();
+$resourceLoader->respond( new ResourceLoaderContext( $resourceLoader, $wgRequest ) );
wfProfileOut( 'load.php' );
wfLogProfilingData();
<?php
class ResourceLoaderTest extends PHPUnit_Framework_TestCase {
+
/* Provider Methods */
public function provide() {