24use Psr\Log\LoggerAwareInterface;
25use Psr\Log\LoggerInterface;
26use Psr\Log\NullLogger;
27use Wikimedia\AtEase\AtEase;
29use Wikimedia\ScopedCallback;
50 protected $origin = self::ORIGIN_CORE_SITEWIDE;
70 const TYPE_SCRIPTS =
'scripts';
72 const TYPE_STYLES =
'styles';
74 const TYPE_COMBINED =
'combined';
77 const LOAD_STYLES =
'styles';
79 const LOAD_GENERAL =
'general';
82 const ORIGIN_CORE_SITEWIDE = 1;
84 const ORIGIN_CORE_INDIVIDUAL = 2;
90 const ORIGIN_USER_SITEWIDE = 3;
92 const ORIGIN_USER_INDIVIDUAL = 4;
94 const ORIGIN_ALL = 10;
132 return MediaWikiServices::getInstance()->getContentLanguage()->getDir() !==
144 wfDeprecated( __METHOD__ .
' without a ResourceLoader context',
'1.34' );
147 if ( $deprecationInfo ) {
149 $warning =
'This page is using the deprecated ResourceLoader module "' .
$name .
'".';
150 if ( is_string( $deprecationInfo ) ) {
151 $warning .=
"\n" . $deprecationInfo;
154 return 'mw.log.warn(' . ResourceLoader::encodeJsonForScript( $warning ) .
');';
156 return 'mw.log.warn(' .
$context->encodeJson( $warning ) .
');';
201 if ( $this->config ===
null ) {
203 $this->config = MediaWikiServices::getInstance()->getMainConfig();
230 if ( !$this->logger ) {
231 $this->logger =
new NullLogger();
253 $derivative->setModules( [ $this->
getName() ] );
254 $derivative->setOnly(
'scripts' );
255 $derivative->setDebug(
true );
300 $derivative->setModules( [ $this->
getName() ] );
301 $derivative->setOnly(
'styles' );
302 $derivative->setDebug(
true );
309 return [
'all' => [ $url ] ];
377 return self::LOAD_GENERAL;
410 if ( !isset( $this->fileDeps[$vary] ) ) {
412 $deps =
$dbr->selectField(
'module_deps',
415 'md_module' => $this->
getName(),
421 if ( !is_null( $deps ) ) {
423 (array)json_decode( $deps,
true )
426 $this->fileDeps[$vary] = [];
429 return $this->fileDeps[$vary];
443 $this->fileDeps[$vary] = $files;
468 $localFileRefs = array_values( array_unique( $localFileRefs ) );
469 sort( $localFileRefs );
473 if ( $localPaths === $storedPaths ) {
480 $cache = ObjectCache::getLocalClusterInstance();
481 $key =
$cache->makeKey( __METHOD__, $this->
getName(), $vary );
482 $scopeLock =
$cache->getScopedLock( $key, 0 );
491 $deps = json_encode( $localPaths, JSON_UNESCAPED_SLASHES );
493 $dbw->upsert(
'module_deps',
495 'md_module' => $this->
getName(),
499 [ [
'md_module',
'md_skin' ] ],
506 if ( $dbw->trxLevel() ) {
507 $dbw->onTransactionResolution(
508 function () use ( &$scopeLock ) {
509 ScopedCallback::consume( $scopeLock );
514 }
catch ( Exception $e ) {
517 wfDebugLog(
'resourceloader', __METHOD__ .
": failed to update DB: $e" );
533 return array_map(
function (
$path ) use (
$IP ) {
534 return RelPath::getRelativePath(
$path,
$IP );
547 return array_map(
function (
$path ) use (
$IP ) {
548 return RelPath::joinPath(
$IP,
$path );
566 if ( !isset( $this->msgBlobs[
$lang] ) ) {
567 $this->
getLogger()->warning(
'Message blob for {module} should have been preloaded', [
570 $store =
$context->getResourceLoader()->getMessageBlobStore();
571 $this->msgBlobs[
$lang] = $store->getBlob( $this,
$lang );
573 return $this->msgBlobs[
$lang];
606 $formattedLinks = [];
608 $link =
"<{$url}>;rel=preload";
609 foreach ( $attribs as $key => $val ) {
610 $link .=
";{$key}={$val}";
612 $formattedLinks[] = $link;
614 if ( $formattedLinks ) {
615 $headers[] =
'Link: ' . implode(
',', $formattedLinks );
686 if ( !array_key_exists( $contextHash, $this->contents ) ) {
687 $this->contents[$contextHash] = $this->
buildContent( $context );
689 return $this->contents[$contextHash];
700 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
701 $statStart = microtime(
true );
716 if (
$context->getDebug() && !
$context->getOnly() && $this->supportsURLLoading() ) {
727 if ( is_string( $scripts )
728 && strlen( $scripts )
729 && substr( $scripts, -1 ) !==
"\n"
739 $stylePairs = $this->
getStyles( $context );
740 if ( count( $stylePairs ) ) {
743 if (
$context->getDebug() && !
$context->getOnly() && $this->supportsURLLoading() ) {
751 foreach ( $stylePairs as $media => $style ) {
753 if ( is_array( $style ) ) {
754 $stylePairs[$media] = [];
755 foreach ( $style as $cssText ) {
756 if ( is_string( $cssText ) ) {
757 $stylePairs[$media][] =
758 ResourceLoader::filter(
'minify-css', $cssText );
761 } elseif ( is_string( $style ) ) {
762 $stylePairs[$media] = ResourceLoader::filter(
'minify-css', $style );
768 'css' => ResourceLoader::makeCombinedStyles( $stylePairs )
790 $statTiming = microtime(
true ) - $statStart;
791 $statName = strtr( $this->
getName(),
'.',
'_' );
792 $stats->timing(
"resourceloader_build.all", 1000 * $statTiming );
793 $stats->timing(
"resourceloader_build.$statName", 1000 * $statTiming );
819 if ( !array_key_exists( $contextHash, $this->versionHash ) ) {
826 if ( !isset( $summary[
'_class'] ) ) {
827 throw new LogicException(
'getDefinitionSummary must call parent method' );
829 $str = json_encode( $summary );
832 $this->versionHash[$contextHash] = ResourceLoader::makeHash( $str );
834 return $this->versionHash[$contextHash];
895 '_class' => static::class,
898 '_cacheVersion' => ResourceLoader::CACHE_VERSION,
926 return $this->
getGroup() ===
'private';
942 if ( !$this->
getConfig()->
get(
'ResourceLoaderValidateJS' ) ) {
945 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
946 return $cache->getWithSetCallback(
948 'resourceloader-jsparse',
949 self::$parseCacheVersion,
954 function () use (
$contents, $fileName ) {
958 AtEase::suppressWarnings();
959 $parser->parse(
$contents, $fileName, 1 );
960 }
catch ( Exception $e ) {
963 AtEase::restoreWarnings();
970 return 'mw.log.error(' .
971 Xml::encodeJsVar(
'JavaScript parse error: ' . $err->getMessage() ) .
983 if ( !self::$jsParser ) {
997 AtEase::suppressWarnings();
998 $mtime = filemtime( $filePath ) ?: 1;
999 AtEase::restoreWarnings();
1023 return implode(
'|', [
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
A mutable version of ResourceLoaderContext.
static getFileContentsHash( $filePaths, $algo='md4')
Get a hash of the combined contents of one or more files, either by retrieving a previously-computed ...
Context object that contains information about the state of a specific ResourceLoader web request.
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
isKnownEmpty(ResourceLoaderContext $context)
Check whether this module is known to be empty.
getDeprecationInformation(ResourceLoaderContext $context=null)
Get JS representing deprecation information for the current module if available.
getScript(ResourceLoaderContext $context)
Get all JS for this module for a given language and skin.
static $parseCacheVersion
getDependencies(ResourceLoaderContext $context=null)
Get a list of modules this module depends on.
enableModuleContentVersion()
Whether to generate version hash based on module content.
getFileDependencies(ResourceLoaderContext $context)
Get the files this module depends on indirectly for a given skin.
getSkipFunction()
Get the skip function.
validateScriptFile( $fileName, $contents)
Validate a given script file; if valid returns the original source.
getMessageBlob(ResourceLoaderContext $context)
Get the hash of the message blob.
static safeFileHash( $filePath)
Compute a non-cryptographic string hash of a file's contents.
static expandRelativePaths(array $filePaths)
Expand directories relative to $IP.
getModuleContent(ResourceLoaderContext $context)
Get an array of this module's resources.
array $msgBlobs
Map of (language => in-object cache for message blob)
supportsURLLoading()
Whether this module supports URL loading.
getMessages()
Get the messages needed for this module.
setName( $name)
Set this module's name.
getStyles(ResourceLoaderContext $context)
Get all CSS for this module for a given skin.
getLessVars(ResourceLoaderContext $context)
Get module-specific LESS variables, if any.
getScriptURLsForDebug(ResourceLoaderContext $context)
Get the URL or URLs to load for this module's JS in debug mode.
getVersionHash(ResourceLoaderContext $context)
Get a string identifying the current version of this module in a given context.
array $versionHash
Map of (context hash => cached module version hash)
getGroup()
Get the group this module is in.
setLogger(LoggerInterface $logger)
getDefinitionSummary(ResourceLoaderContext $context)
Get the definition summary for this module.
static JSParser $jsParser
Lazy-initialized; use self::javaScriptParser()
getFlip(ResourceLoaderContext $context)
array $contents
Map of (context hash => cached module content)
string null $name
Module name.
setConfig(Config $config)
array $fileDeps
Map of (variant => indirect file dependencies)
getOrigin()
Get this module's origin.
getTargets()
Get target(s) for the module, eg ['desktop'] or ['desktop', 'mobile'].
getType()
Get the module's load type.
static javaScriptParser()
setFileDependencies(ResourceLoaderContext $context, $files)
Set in-object cache for file dependencies.
int $origin
Script and style modules form a hierarchy of trustworthiness, with core modules like skins and jQuery...
getStyleURLsForDebug(ResourceLoaderContext $context)
Get the URL or URLs to load for this module's CSS in debug mode.
setMessageBlob( $blob, $lang)
Set in-object cache for message blobs.
getPreloadLinks(ResourceLoaderContext $context)
Get a list of resources that web browsers may preload.
shouldEmbedModule(ResourceLoaderContext $context)
Check whether this module should be embeded rather than linked.
string bool $deprecated
Deprecation string or true if deprecated; false otherwise.
static safeFilemtime( $filePath)
Safe version of filemtime(), which doesn't throw a PHP warning if the file doesn't exist.
getSource()
Get the source of this module.
buildContent(ResourceLoaderContext $context)
Bundle all resources attached to this module into an array.
static getRelativePaths(array $filePaths)
Make file paths relative to MediaWiki directory.
static getVary(ResourceLoaderContext $context)
Get vary string.
saveFileDependencies(ResourceLoaderContext $context, $localFileRefs)
Set the files this module depends on indirectly for a given skin.
getName()
Get this module's name.
getHeaders(ResourceLoaderContext $context)
Get headers to send as part of a module web response.
string[] $targets
What client platforms the module targets (e.g.
getTemplates()
Takes named templates by the module and returns an array mapping.
Interface for configuration instances.
if(!isset( $args[0])) $lang