27use Psr\Log\LoggerAwareInterface;
28use Psr\Log\LoggerInterface;
29use Psr\Log\NullLogger;
32use Wikimedia\Minify\CSSMin;
33use Wikimedia\Minify\JavaScriptMinifier;
35use Wikimedia\Timestamp\ConvertibleTimestamp;
36use Wikimedia\WrappedString;
106 public const CACHE_VERSION = 8;
109 private const RL_DEP_STORE_PREFIX =
'ResourceLoaderModule';
111 private const RL_MODULE_DEP_TTL = BagOStuff::TTL_WEEK;
114 public const FILTER_NOMIN =
'/*@nomin*/';
125 $entitiesByModule = [];
126 foreach ( $moduleNames as $moduleName ) {
127 $entitiesByModule[$moduleName] =
"$moduleName|$vary";
129 $depsByEntity = $this->depStore->retrieveMulti(
130 self::RL_DEP_STORE_PREFIX,
134 foreach ( $moduleNames as $moduleName ) {
135 $module = $this->
getModule( $moduleName );
137 $entity = $entitiesByModule[$moduleName];
138 $deps = $depsByEntity[$entity];
140 $module->setFileDependencies( $context, $paths );
146 ResourceLoaderWikiModule::preloadTitleInfo( $context,
$dbr, $moduleNames );
149 $modulesWithMessages = [];
150 foreach ( $moduleNames as $moduleName ) {
151 $module = $this->
getModule( $moduleName );
152 if ( $module && $module->getMessages() ) {
153 $modulesWithMessages[$moduleName] = $module;
159 $blobs = $store->getBlobs( $modulesWithMessages,
$lang );
160 foreach ( $blobs as $moduleName =>
$blob ) {
161 $modulesWithMessages[$moduleName]->setMessageBlob(
$blob,
$lang );
182 public static function filter( $filter, $data, array $options = [] ) {
183 if ( strpos( $data, self::FILTER_NOMIN ) !==
false ) {
187 if ( isset( $options[
'cache'] ) && $options[
'cache'] ===
false ) {
191 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
194 $key =
$cache->makeGlobalKey(
195 'resourceloader-filter',
201 $result =
$cache->get( $key );
202 if ( $result ===
false ) {
203 $stats->increment(
"resourceloader_cache.$filter.miss" );
205 $cache->set( $key, $result, 24 * 3600 );
207 $stats->increment(
"resourceloader_cache.$filter.hit" );
209 if ( $result ===
null ) {
223 $data = trim( $data );
226 $data = ( $filter ===
'minify-css' )
227 ? CSSMin::minify( $data )
228 : JavaScriptMinifier::minify( $data );
229 }
catch ( Exception $e ) {
230 MWExceptionHandler::logException( $e );
245 LoggerInterface
$logger =
null,
248 $this->logger =
$logger ?:
new NullLogger();
249 $services = MediaWikiServices::getInstance();
253 $this->hookContainer = $services->getHookContainer();
254 $this->hookRunner =
new HookRunner( $this->hookContainer );
257 $this->
addSource(
'local', $config->
get(
'LoadScript' ) );
260 $this->
register(
'startup', [
'class' => ResourceLoaderStartUpModule::class ] );
263 new MessageBlobStore( $this, $this->logger, $services->getMainWANObjectCache() )
336 public function register( $name, array $info = null ) {
338 $registrations = is_array( $name ) ? $name : [ $name => $info ];
339 foreach ( $registrations as $name => $info ) {
341 if ( isset( $this->moduleInfos[$name] ) ) {
343 $this->logger->warning(
344 'ResourceLoader duplicate registration warning. ' .
345 'Another module has already been registered as ' . $name
350 if ( !self::isValidModuleName( $name ) ) {
351 throw new InvalidArgumentException(
"ResourceLoader module name '$name' is invalid, "
352 .
"see ResourceLoader::isValidModuleName()" );
354 if ( !is_array( $info ) ) {
355 throw new InvalidArgumentException(
356 'Invalid module info for "' . $name .
'": expected array, got ' . gettype( $info )
361 $this->moduleInfos[$name] = $info;
372 if ( $this->config->get(
'EnableJavaScriptTest' ) !==
true ) {
373 throw new MWException(
'Attempt to register JavaScript test modules '
374 .
'but <code>$wgEnableJavaScriptTest</code> is false. '
375 .
'Edit your <code>LocalSettings.php</code> to enable it.' );
379 $testModulesMeta = [
'qunit' => [] ];
381 $this->hookRunner->onResourceLoaderTestModules( $testModulesMeta, $this );
382 $extRegistry = ExtensionRegistry::getInstance();
384 $testModules = $testModulesMeta[
'qunit']
385 + $extRegistry->getAttribute(
'QUnitTestModules' );
388 foreach ( $testModules as $name => &$module ) {
390 if ( isset( $module[
'dependencies'] ) && is_string( $module[
'dependencies'] ) ) {
391 $module[
'dependencies'] = [ $module[
'dependencies'] ];
395 $module[
'dependencies'][] =
'mediawiki.qunit-testrunner';
402 $testModules = ( include
"$IP/tests/qunit/QUnitTestResources.php" ) + $testModules;
405 $this->
register( $testModules );
419 public function addSource( $sources, $loadUrl =
null ) {
420 if ( !is_array( $sources ) ) {
421 $sources = [ $sources => $loadUrl ];
423 foreach ( $sources as $id =>
$source ) {
425 if ( isset( $this->sources[$id] ) ) {
426 throw new RuntimeException(
'Cannot register source ' . $id .
' twice' );
431 if ( !isset(
$source[
'loadScript'] ) ) {
432 throw new InvalidArgumentException(
'Each source must have a "loadScript" key' );
445 return array_keys( $this->moduleInfos );
456 return $this->testSuiteModuleNames;
467 return isset( $this->moduleInfos[$name] );
482 if ( !isset( $this->modules[$name] ) ) {
483 if ( !isset( $this->moduleInfos[$name] ) ) {
488 $info = $this->moduleInfos[$name];
489 if ( isset( $info[
'factory'] ) ) {
491 $object = call_user_func( $info[
'factory'], $info );
493 $class = $info[
'class'] ?? ResourceLoaderFileModule::class;
495 $object =
new $class( $info );
497 $object->setConfig( $this->getConfig() );
498 $object->setLogger( $this->logger );
499 $object->setHookContainer( $this->hookContainer );
500 $object->setName( $name );
501 $object->setDependencyAccessCallbacks(
502 [ $this,
'loadModuleDependenciesInternal' ],
503 [ $this,
'saveModuleDependenciesInternal' ]
505 $object->setSkinStylesOverride( $this->moduleSkinStyles );
506 $this->modules[$name] = $object;
509 return $this->modules[$name];
519 $deps = $this->depStore->retrieve( self::RL_DEP_STORE_PREFIX,
"$moduleName|$variant" );
532 $hasPendingUpdate = (bool)$this->depStoreUpdateBuffer;
533 $entity =
"$moduleName|$variant";
535 if ( array_diff( $paths, $priorPaths ) || array_diff( $priorPaths, $paths ) ) {
538 $deps = $this->depStore->newEntityDependencies( $paths, time() );
539 $this->depStoreUpdateBuffer[$entity] = $deps;
541 $this->depStoreUpdateBuffer[$entity] =
null;
543 } elseif ( $priorPaths ) {
545 $this->depStoreUpdateBuffer[$entity] =
'*';
549 if ( !$hasPendingUpdate ) {
550 DeferredUpdates::addCallableUpdate(
function () {
551 $updatesByEntity = $this->depStoreUpdateBuffer;
552 $this->depStoreUpdateBuffer = [];
553 $cache = ObjectCache::getLocalClusterInstance();
559 foreach ( $updatesByEntity as $entity => $update ) {
560 $lockKey =
$cache->makeKey(
'rl-deps', $entity );
561 $scopeLocks[$entity] =
$cache->getScopedLock( $lockKey, 0 );
562 if ( !$scopeLocks[$entity] ) {
567 if ( $update ===
null ) {
568 $entitiesUnreg[] = $entity;
569 } elseif ( $update ===
'*' ) {
570 $entitiesRenew[] = $entity;
572 $depsByEntity[$entity] = $update;
576 $ttl = self::RL_MODULE_DEP_TTL;
577 $this->depStore->storeMulti( self::RL_DEP_STORE_PREFIX, $depsByEntity, $ttl );
578 $this->depStore->remove( self::RL_DEP_STORE_PREFIX, $entitiesUnreg );
579 $this->depStore->renew( self::RL_DEP_STORE_PREFIX, $entitiesRenew, $ttl );
590 return $this->sources;
602 if ( !isset( $this->sources[
$source] ) ) {
603 throw new UnexpectedValueException(
"Unknown source '$source'" );
605 return $this->sources[
$source];
611 public const HASH_LENGTH = 5;
676 $hash = hash(
'fnv132', $value );
680 Wikimedia\base_convert( $hash, 16, 36, self::HASH_LENGTH ),
696 MWExceptionHandler::logException( $e );
697 $this->logger->warning(
699 $context + [
'exception' => $e ]
701 $this->errors[] = self::formatExceptionNoComment( $e );
713 if ( !$moduleNames ) {
716 $hashes = array_map(
function ( $module ) use ( $context ) {
718 return $this->getModule( $module )->getVersionHash( $context );
719 }
catch ( Exception $e ) {
722 $this->outputErrorAndLog( $e,
723 'Calculating version for "{module}" failed: {exception}',
731 return self::makeHash( implode(
'',
$hashes ) );
756 if ( !$this->getModule( $name ) ) {
763 return $this->getCombinedVersion( $context, $filtered );
781 $this->measureResponseTime( RequestContext::getMain()->getTiming() );
787 $module = $this->getModule( $name );
791 if ( $module->getGroup() ===
'private' ) {
793 $this->logger->debug(
"Request for private module '$name' denied" );
794 $this->errors[] =
"Cannot build private module \"$name\"";
805 $this->preloadModuleInfo( array_keys(
$modules ), $context );
806 }
catch ( Exception $e ) {
807 $this->outputErrorAndLog( $e,
'Preloading module info failed: {exception}' );
813 $versionHash = $this->getCombinedVersion( $context, array_keys(
$modules ) );
814 }
catch ( Exception $e ) {
815 $this->outputErrorAndLog( $e,
'Calculating version hash failed: {exception}' );
820 $etag =
'W/"' . $versionHash .
'"';
823 if ( $this->tryRespondNotModified( $context, $etag ) ) {
828 if ( $this->config->get(
'UseFileCache' ) ) {
830 if ( $this->tryRespondFromFileCache( $fileCache, $context, $etag ) ) {
838 $response = $this->makeModuleResponse( $context,
$modules, $missing );
843 $warnings = ob_get_contents();
844 if ( strlen( $warnings ) ) {
845 $this->errors[] = $warnings;
855 if ( $fileCache->isCacheWorthy() ) {
857 $fileCache->saveText( $response );
859 $fileCache->incrMissesRecent( $context->
getRequest() );
863 $this->sendResponseHeaders( $context, $etag, (
bool)$this->errors, $this->extraHeaders );
870 $response = implode(
"\n\n", $this->errors );
871 } elseif ( $this->errors ) {
872 $errorText = implode(
"\n\n", $this->errors );
873 $errorResponse = self::makeComment( $errorText );
875 $errorResponse .=
'if (window.console && console.error) { console.error('
881 $response = $errorResponse . $response;
890 DeferredUpdates::addCallableUpdate(
static function () use ( $timing ) {
891 $measure = $timing->
measure(
'responseTime',
'requestStart',
'requestShutdown' );
892 if ( $measure !==
false ) {
893 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
894 $stats->timing(
'resourceloader.responseTime', $measure[
'duration'] * 1000 );
913 $rlMaxage = $this->config->get(
'ResourceLoaderMaxage' );
922 $maxage = $rlMaxage[
'unversioned'];
926 $maxage = $rlMaxage[
'versioned'];
931 header(
'Content-Type: text/plain; charset=utf-8' );
933 $context->
getImageObj()->sendResponseHeaders( $context );
935 } elseif ( $context->
getOnly() ===
'styles' ) {
936 header(
'Content-Type: text/css; charset=utf-8' );
937 header(
'Access-Control-Allow-Origin: *' );
939 header(
'Content-Type: text/javascript; charset=utf-8' );
943 header(
'ETag: ' . $etag );
946 header(
'Cache-Control: private, no-cache, must-revalidate' );
947 header(
'Pragma: no-cache' );
949 header(
"Cache-Control: public, max-age=$maxage, s-maxage=$maxage" );
950 header(
'Expires: ' . ConvertibleTimestamp::convert( TS_RFC2822, time() + $maxage ) );
952 foreach ( $extra as
$header ) {
970 $clientKeys = $context->
getRequest()->getHeader(
'If-None-Match', WebRequest::GETHEADER_LIST );
972 if ( $clientKeys !==
false && !$context->
getDebug() && in_array( $etag, $clientKeys ) ) {
984 HttpStatus::header( 304 );
986 $this->sendResponseHeaders( $context, $etag,
false );
1005 $rlMaxage = $this->config->get(
'ResourceLoaderMaxage' );
1010 ? $rlMaxage[
'unversioned']
1011 : $rlMaxage[
'versioned'];
1013 $minTime = time() - $maxage;
1014 $good = $fileCache->
isCacheGood( ConvertibleTimestamp::convert( TS_MW, $minTime ) );
1025 $this->sendResponseHeaders( $context, $etag,
false );
1030 $warnings = ob_get_contents();
1031 if ( strlen( $warnings ) ) {
1032 $response = self::makeComment( $warnings ) . $response;
1037 echo $response .
"\n/* Cached {$ts} */";
1055 $encText = str_replace(
'*/',
'* /', $text );
1056 return "/*\n$encText\n*/\n";
1066 return self::makeComment( self::formatExceptionNoComment( $e ) );
1080 return MWExceptionHandler::getPublicLogMessage( $e );
1083 return MWExceptionHandler::getLogMessage( $e ) .
1085 MWExceptionHandler::getRedactedTraceAsString( $e );
1100 array
$modules, array $missing = []
1105 if (
$modules === [] && $missing === [] ) {
1115 $data = $image->getImageData( $context );
1116 if ( $data ===
false ) {
1118 $this->errors[] =
'Image generation failed';
1123 foreach ( $missing as $name ) {
1124 $states[$name] =
'missing';
1127 $filter = $context->
getOnly() ===
'styles' ?
'minify-css' :
'minify-js';
1129 foreach (
$modules as $name => $module ) {
1131 $content = $module->getModuleContent( $context );
1132 $implementKey = $name .
'@' . $module->getVersionHash( $context );
1135 if ( isset(
$content[
'headers'] ) ) {
1136 $this->extraHeaders = array_merge( $this->extraHeaders,
$content[
'headers'] );
1140 switch ( $context->
getOnly() ) {
1143 if ( is_string( $scripts ) ) {
1145 $strContent = $scripts;
1146 } elseif ( is_array( $scripts ) ) {
1148 $strContent = self::makeLoaderImplementScript(
1163 $strContent = isset( $styles[
'css'] ) ? implode(
'', $styles[
'css'] ) :
'';
1166 $scripts =
$content[
'scripts'] ??
'';
1167 if ( is_string( $scripts ) ) {
1168 if ( $name ===
'site' || $name ===
'user' ) {
1174 $scripts = self::filter(
'minify-js', $scripts );
1180 $strContent = self::makeLoaderImplementScript(
1193 $strContent = self::filter( $filter, $strContent );
1197 $strContent = self::ensureNewline( $strContent );
1200 if ( $context->
getOnly() ===
'scripts' ) {
1202 $out .= self::ensureNewline( $strContent );
1204 $out .= $strContent;
1207 }
catch ( Exception $e ) {
1208 $this->outputErrorAndLog( $e,
'Generating module package failed: {exception}' );
1211 $states[$name] =
'error';
1221 foreach (
$modules as $name => $module ) {
1222 $states[$name] =
'ready';
1228 $stateScript = self::makeLoaderStateScript( $context, $states );
1230 $stateScript = self::filter(
'minify-js', $stateScript );
1233 $out = self::ensureNewline( $out ) . $stateScript;
1235 } elseif ( $states ) {
1236 $this->errors[] =
'Problematic modules: '
1249 public static function ensureNewline( $str ) {
1250 $end = substr( $str, -1 );
1251 if ( $end ===
false || $end ===
'' || $end ===
"\n" ) {
1263 public function getModulesByMessage( $messageKey ) {
1265 foreach ( $this->getModuleNames() as $moduleName ) {
1266 $module = $this->getModule( $moduleName );
1267 if ( in_array( $messageKey, $module->getMessages() ) ) {
1268 $moduleNames[] = $moduleName;
1271 return $moduleNames;
1291 private static function makeLoaderImplementScript(
1295 if ( $scripts->value ===
'' ) {
1297 } elseif ( $context->
getDebug() ) {
1299 $scripts =
new XmlJsCode(
"function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
1303 'function($,jQuery,require,module){' . self::ensureNewline( $scripts->value ) .
'}'
1306 } elseif ( is_array( $scripts ) && isset( $scripts[
'files'] ) ) {
1307 $files = $scripts[
'files'];
1311 if (
$file[
'type'] ===
'script' ) {
1317 $file =
new XmlJsCode(
"function ( require, module ) {\n$content}" );
1326 'main' => $scripts[
'main'],
1329 } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) {
1330 throw new InvalidArgumentException(
'Script must be a string or an array of URLs' );
1343 self::trimArray( $module );
1354 public static function makeMessageSetScript( $messages ) {
1355 return 'mw.messages.set('
1356 . self::encodeJsonForScript( (
object)$messages )
1367 public static function makeCombinedStyles( array $stylePairs ) {
1369 foreach ( $stylePairs as $media => $styles ) {
1373 $styles = (array)$styles;
1374 foreach ( $styles as $style ) {
1375 $style = trim( $style );
1377 if ( $style !==
'' ) {
1382 if ( $media ===
'' || $media ==
'all' ) {
1384 } elseif ( is_string( $media ) ) {
1385 $out[] =
"@media $media {\n" . str_replace(
"\n",
"\n\t",
"\t" . $style ) .
"}";
1403 public static function encodeJsonForScript( $data ) {
1413 $jsonFlags = JSON_UNESCAPED_SLASHES |
1414 JSON_UNESCAPED_UNICODE |
1417 if ( self::inDebugMode() ) {
1418 $jsonFlags |= JSON_PRETTY_PRINT;
1420 return json_encode( $data, $jsonFlags );
1435 public static function makeLoaderStateScript(
1438 return 'mw.loader.state('
1443 private static function isEmptyObject( stdClass $obj ) {
1444 foreach ( $obj as $key => $value ) {
1463 private static function trimArray( array &$array ): void {
1464 $i = count( $array );
1466 if ( $array[$i] ===
null
1467 || $array[$i] === []
1468 || ( $array[$i] instanceof
XmlJsCode && $array[$i]->value ===
'{}' )
1469 || ( $array[$i] instanceof stdClass && self::isEmptyObject( $array[$i] ) )
1471 unset( $array[$i] );
1503 public static function makeLoaderRegisterScript(
1510 foreach (
$modules as $i => &$module ) {
1512 $index[$module[0]] = $i;
1515 if ( isset( $module[2] ) ) {
1516 foreach ( $module[2] as &$dependency ) {
1517 if ( isset( $index[$dependency] ) ) {
1519 $dependency = $index[$dependency];
1525 array_walk(
$modules, [ self::class,
'trimArray' ] );
1527 return 'mw.loader.register('
1545 public static function makeLoaderSourcesScript(
1548 return 'mw.loader.addSource('
1559 public static function makeLoaderConditionalScript( $script ) {
1561 return '(RLQ=window.RLQ||[]).push(function(){' .
1562 trim( $script ) .
'});';
1573 public static function makeInlineCodeWithModule(
$modules, $script ) {
1575 return '(RLQ=window.RLQ||[]).push(['
1576 . self::encodeJsonForScript(
$modules ) .
','
1577 .
'function(){' . trim( $script ) .
'}'
1592 public static function makeInlineScript( $script, $nonce =
null ) {
1593 $js = self::makeLoaderConditionalScript( $script );
1595 if ( $nonce ===
null ) {
1596 wfWarn( __METHOD__ .
" did not get nonce. Will break CSP" );
1597 } elseif ( $nonce !==
false ) {
1601 $escNonce =
' nonce="' . htmlspecialchars( $nonce ) .
'"';
1604 return new WrappedString(
1605 Html::inlineScript( $js, $nonce ),
1606 "<script$escNonce>(RLQ=window.RLQ||[]).push(function(){",
1619 public static function makeConfigSetScript( array $configuration ) {
1620 $json = self::encodeJsonForScript( $configuration );
1621 if ( $json ===
false ) {
1623 'JSON serialization of config data failed. ' .
1624 'This usually means the config data is not valid UTF-8.'
1627 return 'mw.log.error(' . self::encodeJsonForScript( $e->__toString() ) .
');';
1629 return "mw.config.set($json);";
1645 public static function makePackedModulesString( array
$modules ) {
1648 $pos = strrpos( $module,
'.' );
1649 $prefix = $pos ===
false ?
'' : substr( $module, 0, $pos );
1650 $suffix = $pos ===
false ? $module : substr( $module, $pos + 1 );
1651 $moduleMap[$prefix][] = $suffix;
1655 foreach ( $moduleMap as $prefix => $suffixes ) {
1656 $p = $prefix ===
'' ?
'' : $prefix .
'.';
1657 $arr[] = $p . implode(
',', $suffixes );
1659 return implode(
'|', $arr );
1673 public static function expandModuleNames(
$modules ) {
1675 $exploded = explode(
'|',
$modules );
1676 foreach ( $exploded as $group ) {
1677 if ( strpos( $group,
',' ) ===
false ) {
1683 $pos = strrpos( $group,
'.' );
1684 if ( $pos ===
false ) {
1686 $retval = array_merge( $retval, explode(
',', $group ) );
1689 $prefix = substr( $group, 0, $pos );
1690 $suffixes = explode(
',', substr( $group, $pos + 1 ) );
1691 foreach ( $suffixes as $suffix ) {
1692 $retval[] =
"$prefix.$suffix";
1710 public static function inDebugMode() {
1711 if ( self::$debugMode ===
null ) {
1718 return self::$debugMode;
1731 public static function clearCache() {
1732 self::$debugMode =
null;
1745 array $extraQuery = []
1747 $query = self::createLoaderQuery( $context, $extraQuery );
1748 $script = $this->getLoadScript(
$source );
1762 protected static function createLoaderQuery(
1765 return self::makeLoaderQuery(
1773 $context->
getRequest()->getBool(
'printable' ),
1774 $context->
getRequest()->getBool(
'handheld' ),
1795 public static function makeLoaderQuery( array
$modules,
$lang, $skin, $user =
null,
1796 $version =
null,
$debug = ResourceLoaderContext::DEBUG_OFF, $only =
null,
1797 $printable =
false, $handheld =
false, array $extraQuery = []
1800 'modules' => self::makePackedModulesString(
$modules ),
1806 if (
$lang !== ResourceLoaderContext::DEFAULT_LANG ) {
1807 $query[
'lang'] =
$lang;
1809 if ( $skin !== ResourceLoaderContext::DEFAULT_SKIN ) {
1810 $query[
'skin'] = $skin;
1812 if (
$debug !== ResourceLoaderContext::DEBUG_OFF ) {
1813 $query[
'debug'] = strval(
$debug );
1815 if ( $user !==
null ) {
1816 $query[
'user'] = $user;
1818 if ( $version !==
null ) {
1819 $query[
'version'] = $version;
1821 if ( $only !==
null ) {
1822 $query[
'only'] = $only;
1825 $query[
'printable'] = 1;
1828 $query[
'handheld'] = 1;
1830 $query += $extraQuery;
1846 public static function isValidModuleName( $moduleName ) {
1847 $len = strlen( $moduleName );
1848 return $len <= 255 && strcspn( $moduleName,
'!,|', 0, $len ) === $len;
1862 public function getLessCompiler( array $vars = [], array $importDirs = [] ) {
1867 if ( !class_exists( Less_Parser::class ) ) {
1868 throw new MWException(
'MediaWiki requires the less.php parser' );
1871 $importDirs[] =
"$IP/resources/src/mediawiki.less";
1873 $parser =
new Less_Parser;
1874 $parser->ModifyVars( $vars );
1876 $parser->SetImportDirs( array_fill_keys( $importDirs,
'' ) );
1877 $parser->SetOption(
'relativeUrls',
false );
1890 public static function getSiteConfigSettings(
1897 $namespaceIds = $contLang->getNamespaceIds();
1898 $caseSensitiveNamespaces = [];
1900 foreach ( $nsInfo->getCanonicalNamespaces() as $index => $name ) {
1901 $namespaceIds[$contLang->lc( $name )] = $index;
1902 if ( !$nsInfo->isCapitalized( $index ) ) {
1903 $caseSensitiveNamespaces[] = $index;
1907 $illegalFileChars = $conf->
get(
'IllegalFileChars' );
1916 'stylepath' => $conf->
get(
'StylePath' ),
1917 'wgArticlePath' => $conf->
get(
'ArticlePath' ),
1918 'wgScriptPath' => $conf->
get(
'ScriptPath' ),
1919 'wgScript' => $conf->
get(
'Script' ),
1920 'wgSearchType' => $conf->
get(
'SearchType' ),
1921 'wgVariantArticlePath' => $conf->
get(
'VariantArticlePath' ),
1922 'wgServer' => $conf->
get(
'Server' ),
1923 'wgServerName' => $conf->
get(
'ServerName' ),
1925 'wgContentLanguage' => $contLang->getCode(),
1927 'wgFormattedNamespaces' => $contLang->getFormattedNamespaces(),
1928 'wgNamespaceIds' => $namespaceIds,
1929 'wgContentNamespaces' => $nsInfo->getContentNamespaces(),
1930 'wgSiteName' => $conf->
get(
'Sitename' ),
1931 'wgDBname' => $conf->
get(
'DBname' ),
1933 'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
1934 'wgCommentByteLimit' =>
null,
1936 'wgExtensionAssetsPath' => $conf->
get(
'ExtensionAssetsPath' ),
1947 'wgActionPaths' => (object)$conf->
get(
'ActionPaths' ),
1949 'wgTranslateNumerals' => $conf->
get(
'TranslateNumerals' ),
1951 'wgExtraSignatureNamespaces' => $conf->
get(
'ExtraSignatureNamespaces' ),
1953 'wgCookiePrefix' => $conf->
get(
'CookiePrefix' ),
1954 'wgCookieDomain' => $conf->
get(
'CookieDomain' ),
1955 'wgCookiePath' => $conf->
get(
'CookiePath' ),
1956 'wgCookieExpiration' => $conf->
get(
'CookieExpiration' ),
1961 'wgForeignUploadTargets' => $conf->
get(
'ForeignUploadTargets' ),
1962 'wgEnableUploads' => $conf->
get(
'EnableUploads' ),
1965 Hooks::runner()->onResourceLoaderGetConfigVars( $vars, $skin, $conf );
$wgResourceLoaderDebug
The default debug mode (on/off) for of ResourceLoader requests.
$wgShowExceptionDetails
If set to true, uncaught exceptions will print the exception message and a complete stack trace to ou...
const MW_VERSION
The running version of MediaWiki.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfUrlProtocols( $includeProtocolRelative=true)
Returns a regular expression of url protocols.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
isCacheGood( $timestamp='')
Check if up to date cache file exists.
fetchText()
Get the uncompressed text from the cache.
cacheTimestamp()
Get the last-modified timestamp of the cache file.
Simple store for keeping values in an associative array for the current process.
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).
This class generates message blobs for use by ResourceLoader.
static transformCssMedia( $media)
Transform "media" attribute based on request parameters.
ResourceLoader request result caching in the file system.
static useFileCache(ResourceLoaderContext $context)
Check if an RL request can be cached.
static newFromContext(ResourceLoaderContext $context)
Construct an ResourceFileCache from a context.
Context object that contains information about the state of a specific ResourceLoader web request.
static debugFromString(?string $debug)
getImageObj()
If this is a request for an image, get the ResourceLoaderImage object.
encodeJson( $data)
Wrapper around json_encode that avoids needless escapes, and pretty-prints in debug mode.
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
static expandRelativePaths(array $filePaths)
Expand directories relative to $IP.
static getVary(ResourceLoaderContext $context)
Get vary string.
ResourceLoader is a loading system for JavaScript and CSS resources.
setDependencyStore(DependencyStore $tracker)
addSource( $sources, $loadUrl=null)
Add a foreign source of modules.
__construct(Config $config, LoggerInterface $logger=null, DependencyStore $tracker=null)
Register core modules and runs registration hooks.
static formatException(Throwable $e)
Handle exception display.
tryRespondFromFileCache(ResourceFileCache $fileCache, ResourceLoaderContext $context, $etag)
Send out code for a response from file cache if possible.
setMessageBlobStore(MessageBlobStore $blobStore)
makeModuleResponse(ResourceLoaderContext $context, array $modules, array $missing=[])
Generate code for a response.
setLogger(LoggerInterface $logger)
HookContainer $hookContainer
getLoadScript( $source)
Get the URL to the load.php endpoint for the given ResourceLoader source.
sendResponseHeaders(ResourceLoaderContext $context, $etag, $errors, array $extra=[])
Send main response headers to the client.
array[] $moduleInfos
Map of (module name => associative info array)
tryRespondNotModified(ResourceLoaderContext $context, $etag)
Respond with HTTP 304 Not Modified if appropiate.
getSources()
Get the list of sources.
loadModuleDependenciesInternal( $moduleName, $variant)
DependencyStore $depStore
static int null $debugMode
string[] $extraHeaders
Extra HTTP response headers from modules loaded in makeModuleResponse()
outputErrorAndLog(Exception $e, $msg, array $context=[])
Add an error to the 'errors' array and log it.
array $testModuleNames
Associative array mapping framework ids to a list of names of test suite modules like [ 'qunit' => [ ...
getTestSuiteModuleNames()
Get a list of module names with QUnit test suites.
saveModuleDependenciesInternal( $moduleName, $variant, $paths, $priorPaths)
string[] $testSuiteModuleNames
List of module names that contain QUnit test suites.
getModule( $name)
Get the ResourceLoaderModule object for a given module name.
array $sources
Map of (source => path); E.g.
isModuleRegistered( $name)
Check whether a ResourceLoader module is registered.
static applyFilter( $filter, $data)
measureResponseTime(Timing $timing)
setModuleSkinStyles(array $moduleSkinStyles)
ResourceLoaderModule[] $modules
Map of (module name => ResourceLoaderModule)
static formatExceptionNoComment(Throwable $e)
Handle exception display.
array $moduleSkinStyles
Styles that are skin-specific and supplement or replace the default skinStyles of a FileModule.
array $depStoreUpdateBuffer
Map of (module-variant => buffered DependencyStore updates)
MessageBlobStore $blobStore
array $errors
Errors accumulated during current respond() call.
static makeComment( $text)
Generate a CSS or JS comment block.
getCombinedVersion(ResourceLoaderContext $context, array $moduleNames)
Helper method to get and combine versions of multiple modules.
respond(ResourceLoaderContext $context)
Output a response to a load request, including the content-type header.
static makeHash( $value)
Create a hash for module versioning purposes.
makeVersionQuery(ResourceLoaderContext $context, array $modules)
Get the expected value of the 'version' query parameter.
static filter( $filter, $data, array $options=[])
Run JavaScript or CSS data through a filter, caching the filtered result for future calls.
preloadModuleInfo(array $moduleNames, ResourceLoaderContext $context)
Load information stored in the database and dependency tracking store about modules.
An interface to help developers measure the performance of their applications.
measure( $measureName, $startMark='requestStart', $endMark=null)
This method stores the duration between two marks along with the associated name (a "measure").
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
static getCurrentWikiId()
A wrapper class which causes Xml::encodeJsVar() and Xml::encodeJsCall() to interpret a given string a...
static encodeObject( $obj, $pretty=false)
Encode an object containing XmlJsCode objects.
static encodeJsCall( $name, $args, $pretty=false)
Create a call to a JavaScript function.
Interface for configuration instances.
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!isset( $args[0])) $lang
if(count( $args)< 1) $tracker