MediaWiki  master
ResourceLoaderFileModule.php
Go to the documentation of this file.
1 <?php
25 use Wikimedia\Minify\CSSMin;
26 
42  protected $localBasePath = '';
43 
45  protected $remoteBasePath = '';
46 
48  protected $templates = [];
49 
57  protected $scripts = [];
58 
66  protected $languageScripts = [];
67 
75  protected $skinScripts = [];
76 
84  protected $debugScripts = [];
85 
93  protected $styles = [];
94 
102  protected $skinStyles = [];
103 
111  protected $packageFiles = null;
112 
117  private $expandedPackageFiles = [];
118 
124 
132  protected $dependencies = [];
133 
137  protected $skipFunction = null;
138 
146  protected $messages = [];
147 
149  protected $group;
150 
152  protected $debugRaw = true;
153 
155  protected $targets = [ 'desktop' ];
156 
158  protected $noflip = false;
159 
161  protected $es6 = false;
162 
167  protected $hasGeneratedStyles = false;
168 
176  protected $localFileRefs = [];
177 
182  protected $missingLocalFileRefs = [];
183 
187  protected $vueComponentParser = null;
188 
200  public function __construct(
201  array $options = [],
202  $localBasePath = null,
203  $remoteBasePath = null
204  ) {
205  // Flag to decide whether to automagically add the mediawiki.template module
206  $hasTemplates = false;
207  // localBasePath and remoteBasePath both have unbelievably long fallback chains
208  // and need to be handled separately.
209  list( $this->localBasePath, $this->remoteBasePath ) =
211 
212  // Extract, validate and normalise remaining options
213  foreach ( $options as $member => $option ) {
214  switch ( $member ) {
215  // Lists of file paths
216  case 'scripts':
217  case 'debugScripts':
218  case 'styles':
219  case 'packageFiles':
220  $this->{$member} = is_array( $option ) ? $option : [ $option ];
221  break;
222  case 'templates':
223  $hasTemplates = true;
224  $this->{$member} = is_array( $option ) ? $option : [ $option ];
225  break;
226  // Collated lists of file paths
227  case 'languageScripts':
228  case 'skinScripts':
229  case 'skinStyles':
230  if ( !is_array( $option ) ) {
231  throw new InvalidArgumentException(
232  "Invalid collated file path list error. " .
233  "'$option' given, array expected."
234  );
235  }
236  foreach ( $option as $key => $value ) {
237  if ( !is_string( $key ) ) {
238  throw new InvalidArgumentException(
239  "Invalid collated file path list key error. " .
240  "'$key' given, string expected."
241  );
242  }
243  $this->{$member}[$key] = is_array( $value ) ? $value : [ $value ];
244  }
245  break;
246  case 'deprecated':
247  $this->deprecated = $option;
248  break;
249  // Lists of strings
250  case 'dependencies':
251  case 'messages':
252  case 'targets':
253  // Normalise
254  $option = array_values( array_unique( (array)$option ) );
255  sort( $option );
256 
257  $this->{$member} = $option;
258  break;
259  // Single strings
260  case 'group':
261  case 'skipFunction':
262  $this->{$member} = (string)$option;
263  break;
264  // Single booleans
265  case 'debugRaw':
266  case 'noflip':
267  case 'es6':
268  $this->{$member} = (bool)$option;
269  break;
270  }
271  }
272  if ( isset( $options['scripts'] ) && isset( $options['packageFiles'] ) ) {
273  throw new InvalidArgumentException( "A module may not set both 'scripts' and 'packageFiles'" );
274  }
275  if ( isset( $options['packageFiles'] ) && isset( $options['skinScripts'] ) ) {
276  throw new InvalidArgumentException( "Options 'skinScripts' and 'packageFiles' cannot be used together." );
277  }
278  if ( $hasTemplates ) {
279  $this->dependencies[] = 'mediawiki.template';
280  // Ensure relevant template compiler module gets loaded
281  foreach ( $this->templates as $alias => $templatePath ) {
282  if ( is_int( $alias ) ) {
283  $alias = $this->getPath( $templatePath );
284  }
285  $suffix = explode( '.', $alias );
286  $suffix = end( $suffix );
287  $compilerModule = 'mediawiki.template.' . $suffix;
288  if ( $suffix !== 'html' && !in_array( $compilerModule, $this->dependencies ) ) {
289  $this->dependencies[] = $compilerModule;
290  }
291  }
292  }
293  }
294 
306  public static function extractBasePaths(
307  array $options = [],
308  $localBasePath = null,
309  $remoteBasePath = null
310  ) {
311  global $IP, $wgResourceBasePath;
312 
313  // The different ways these checks are done, and their ordering, look very silly,
314  // but were preserved for backwards-compatibility just in case. Tread lightly.
315 
316  if ( $localBasePath === null ) {
318  }
319  if ( $remoteBasePath === null ) {
321  }
322 
323  if ( isset( $options['remoteExtPath'] ) ) {
324  global $wgExtensionAssetsPath;
325  $remoteBasePath = $wgExtensionAssetsPath . '/' . $options['remoteExtPath'];
326  }
327 
328  if ( isset( $options['remoteSkinPath'] ) ) {
329  global $wgStylePath;
330  $remoteBasePath = $wgStylePath . '/' . $options['remoteSkinPath'];
331  }
332 
333  if ( array_key_exists( 'localBasePath', $options ) ) {
334  $localBasePath = (string)$options['localBasePath'];
335  }
336 
337  if ( array_key_exists( 'remoteBasePath', $options ) ) {
338  $remoteBasePath = (string)$options['remoteBasePath'];
339  }
340 
341  if ( $remoteBasePath === '' ) {
342  // If MediaWiki is installed at the document root (not recommended),
343  // then wgScriptPath is set to the empty string by the installer to
344  // ensure safe concatenating of file paths (avoid "/" + "/foo" being "//foo").
345  // However, this also means the path itself can be an invalid URI path,
346  // as those must start with a slash. Within ResourceLoader, we will not
347  // do such primitive/unsafe slash concatenation and use URI resolution
348  // instead, so beyond this point, to avoid fatal errors in CSSMin::resolveUrl(),
349  // do a best-effort support for docroot installs by casting this to a slash.
350  $remoteBasePath = '/';
351  }
352 
353  return [ $localBasePath, $remoteBasePath ];
354  }
355 
362  public function getScript( ResourceLoaderContext $context ) {
363  $deprecationScript = $this->getDeprecationInformation( $context );
364  if ( $this->packageFiles !== null ) {
365  $packageFiles = $this->getPackageFiles( $context );
366  foreach ( $packageFiles['files'] as &$file ) {
367  if ( $file['type'] === 'script+style' ) {
368  $file['content'] = $file['content']['script'];
369  $file['type'] = 'script';
370  }
371  }
372  if ( $deprecationScript ) {
373  $mainFile =& $packageFiles['files'][$packageFiles['main']];
374  $mainFile['content'] = $deprecationScript . $mainFile['content'];
375  }
376  return $packageFiles;
377  }
378 
379  $files = $this->getScriptFiles( $context );
380  return $deprecationScript . $this->readScriptFiles( $files );
381  }
382 
387  public function getScriptURLsForDebug( ResourceLoaderContext $context ) {
388  $rl = $context->getResourceLoader();
389  $config = $this->getConfig();
390  $server = $config->get( 'Server' );
391 
392  $urls = [];
393  foreach ( $this->getScriptFiles( $context ) as $file ) {
394  $url = OutputPage::transformResourcePath( $config, $this->getRemotePath( $file ) );
395  // Expand debug URL in case we are another wiki's module source (T255367)
396  $url = $rl->expandUrl( $server, $url );
397  $urls[] = $url;
398  }
399  return $urls;
400  }
401 
405  public function supportsURLLoading() {
406  // If package files are involved, don't support URL loading, because that breaks
407  // scoped require() functions
408  return $this->debugRaw && !$this->packageFiles;
409  }
410 
417  public function getStyles( ResourceLoaderContext $context ) {
418  $styles = $this->readStyleFiles(
419  $this->getStyleFiles( $context ),
420  $context
421  );
422 
423  if ( $this->packageFiles !== null ) {
424  $packageFiles = $this->getPackageFiles( $context );
425  foreach ( $packageFiles['files'] as $fileName => $file ) {
426  if ( $file['type'] === 'script+style' ) {
427  $style = $this->processStyle(
428  $file['content']['style'],
429  $file['content']['styleLang'],
430  $fileName,
431  $context
432  );
433  $styles['all'] = ( $styles['all'] ?? '' ) . "\n" . $style;
434  }
435  }
436  }
437 
438  // Track indirect file dependencies so that ResourceLoaderStartUpModule can check for
439  // on-disk file changes to any of this files without having to recompute the file list
440  $this->saveFileDependencies( $context, $this->localFileRefs );
441 
442  return $styles;
443  }
444 
449  public function getStyleURLsForDebug( ResourceLoaderContext $context ) {
450  if ( $this->hasGeneratedStyles ) {
451  // Do the default behaviour of returning a url back to load.php
452  // but with only=styles.
453  return parent::getStyleURLsForDebug( $context );
454  }
455  // Our module consists entirely of real css files,
456  // in debug mode we can load those directly.
457  $urls = [];
458  foreach ( $this->getStyleFiles( $context ) as $mediaType => $list ) {
459  $urls[$mediaType] = [];
460  foreach ( $list as $file ) {
461  $urls[$mediaType][] = OutputPage::transformResourcePath(
462  $this->getConfig(),
463  $this->getRemotePath( $file )
464  );
465  }
466  }
467  return $urls;
468  }
469 
475  public function getMessages() {
476  return $this->messages;
477  }
478 
484  public function getGroup() {
485  return $this->group;
486  }
487 
493  public function getDependencies( ResourceLoaderContext $context = null ) {
494  return $this->dependencies;
495  }
496 
505  private function getFileContents( $localPath, $type ) {
506  if ( !is_file( $localPath ) ) {
507  throw new RuntimeException(
508  __METHOD__ . ": $type file not found, or is not a file: \"$localPath\""
509  );
510  }
511  return $this->stripBom( file_get_contents( $localPath ) );
512  }
513 
518  public function getSkipFunction() {
519  if ( !$this->skipFunction ) {
520  return null;
521  }
522  $localPath = $this->getLocalPath( $this->skipFunction );
523  return $this->getFileContents( $localPath, 'skip function' );
524  }
525 
526  public function requiresES6() {
527  return $this->es6;
528  }
529 
538  public function enableModuleContentVersion() {
539  return false;
540  }
541 
548  private function getFileHashes( ResourceLoaderContext $context ) {
549  $files = [];
550 
551  $styleFiles = $this->getStyleFiles( $context );
552  foreach ( $styleFiles as $paths ) {
553  $files = array_merge( $files, $paths );
554  }
555 
556  // Extract file paths for package files
557  // Optimisation: Use foreach() and isset() instead of array_map/array_filter.
558  // This is a hot code path, called by StartupModule for thousands of modules.
559  $expandedPackageFiles = $this->expandPackageFiles( $context );
560  $packageFiles = [];
561  if ( $expandedPackageFiles ) {
562  foreach ( $expandedPackageFiles['files'] as $fileInfo ) {
563  if ( isset( $fileInfo['filePath'] ) ) {
564  $packageFiles[] = $fileInfo['filePath'];
565  }
566  }
567  }
568 
569  // Merge all the file paths we were able discover directly from the module definition.
570  // This is the master list of direct-dependent files for this module.
571  $files = array_merge(
572  $files,
574  $this->scripts,
575  $this->templates,
576  $context->getDebug() ? $this->debugScripts : [],
577  $this->getLanguageScripts( $context->getLanguage() ),
578  self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' )
579  );
580  if ( $this->skipFunction ) {
581  $files[] = $this->skipFunction;
582  }
583 
584  // Expand these local paths into absolute file paths
585  $files = array_map( [ $this, 'getLocalPath' ], $files );
586 
587  // Add any lazily discovered file dependencies from previous module builds.
588  // These are added last because they are already absolute file paths.
589  $files = array_merge( $files, $this->getFileDependencies( $context ) );
590 
591  // Filter out any duplicates. Typically introduced by getFileDependencies() which
592  // may lazily re-discover a master file.
593  $files = array_unique( $files );
594 
595  // Don't return array keys or any other form of file path here, only the hashes.
596  // Including file paths would needlessly cause global cache invalidation when files
597  // move on disk or if e.g. the MediaWiki directory name changes.
598  // Anything where order is significant is already detected by the definition summary.
600  }
601 
608  public function getDefinitionSummary( ResourceLoaderContext $context ) {
609  $summary = parent::getDefinitionSummary( $context );
610 
611  $options = [];
612  foreach ( [
613  // The following properties are omitted because they don't affect the module reponse:
614  // - localBasePath (Per T104950; Changes when absolute directory name changes. If
615  // this affects 'scripts' and other file paths, getFileHashes accounts for that.)
616  // - remoteBasePath (Per T104950)
617  // - dependencies (provided via startup module)
618  // - targets
619  // - group (provided via startup module)
620  'scripts',
621  'debugScripts',
622  'styles',
623  'languageScripts',
624  'skinScripts',
625  'skinStyles',
626  'messages',
627  'templates',
628  'skipFunction',
629  'debugRaw',
630  ] as $member ) {
631  $options[$member] = $this->{$member};
632  }
633 
634  $packageFiles = $this->expandPackageFiles( $context );
635  if ( $packageFiles ) {
636  // Extract the minimum needed:
637  // - The 'main' pointer (included as-is).
638  // - The 'files' array, simplified to only which files exist (the keys of
639  // this array), and something that represents their non-file content.
640  // For packaged files that reflect files directly from disk, the
641  // 'getFileHashes' method tracks their content already.
642  // It is important that the keys of the $packageFiles['files'] array
643  // are preserved, as they do affect the module output.
644  $packageFiles['files'] = array_map( static function ( $fileInfo ) {
645  return $fileInfo['definitionSummary'] ?? ( $fileInfo['content'] ?? null );
646  }, $packageFiles['files'] );
647  }
648 
649  $summary[] = [
650  'options' => $options,
651  'packageFiles' => $packageFiles,
652  'fileHashes' => $this->getFileHashes( $context ),
653  'messageBlob' => $this->getMessageBlob( $context ),
654  ];
655 
656  $lessVars = $this->getLessVars( $context );
657  if ( $lessVars ) {
658  $summary[] = [ 'lessVars' => $lessVars ];
659  }
660 
661  return $summary;
662  }
663 
667  protected function getVueComponentParser() {
668  if ( $this->vueComponentParser === null ) {
669  $this->vueComponentParser = new VueComponentParser;
670  }
672  }
673 
678  protected function getPath( $path ) {
679  if ( $path instanceof ResourceLoaderFilePath ) {
680  return $path->getPath();
681  }
682 
683  return $path;
684  }
685 
690  protected function getLocalPath( $path ) {
691  if ( $path instanceof ResourceLoaderFilePath ) {
692  return $path->getLocalPath();
693  }
694 
695  return "{$this->localBasePath}/$path";
696  }
697 
702  protected function getRemotePath( $path ) {
703  if ( $path instanceof ResourceLoaderFilePath ) {
704  return $path->getRemotePath();
705  }
706 
707  if ( $this->remoteBasePath === '/' ) {
708  return "/$path";
709  } else {
710  return "{$this->remoteBasePath}/$path";
711  }
712  }
713 
721  public function getStyleSheetLang( $path ) {
722  return preg_match( '/\.less$/i', $path ) ? 'less' : 'css';
723  }
724 
730  public static function getPackageFileType( $path ) {
731  if ( preg_match( '/\.json$/i', $path ) ) {
732  return 'data';
733  }
734  if ( preg_match( '/\.vue$/i', $path ) ) {
735  return 'script-vue';
736  }
737  return 'script';
738  }
739 
749  protected static function collateFilePathListByOption( array $list, $option, $default ) {
750  $collatedFiles = [];
751  foreach ( $list as $key => $value ) {
752  if ( is_int( $key ) ) {
753  // File name as the value
754  if ( !isset( $collatedFiles[$default] ) ) {
755  $collatedFiles[$default] = [];
756  }
757  $collatedFiles[$default][] = $value;
758  } elseif ( is_array( $value ) ) {
759  // File name as the key, options array as the value
760  $optionValue = $value[$option] ?? $default;
761  if ( !isset( $collatedFiles[$optionValue] ) ) {
762  $collatedFiles[$optionValue] = [];
763  }
764  $collatedFiles[$optionValue][] = $key;
765  }
766  }
767  return $collatedFiles;
768  }
769 
779  protected static function tryForKey( array $list, $key, $fallback = null ) {
780  if ( isset( $list[$key] ) && is_array( $list[$key] ) ) {
781  return $list[$key];
782  } elseif ( is_string( $fallback )
783  && isset( $list[$fallback] )
784  && is_array( $list[$fallback] )
785  ) {
786  return $list[$fallback];
787  }
788  return [];
789  }
790 
797  private function getScriptFiles( ResourceLoaderContext $context ) {
798  $files = array_merge(
799  $this->scripts,
800  $this->getLanguageScripts( $context->getLanguage() ),
801  self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' )
802  );
803  if ( $context->getDebug() ) {
804  $files = array_merge( $files, $this->debugScripts );
805  }
806 
807  return array_unique( $files, SORT_REGULAR );
808  }
809 
817  private function getLanguageScripts( $lang ) {
818  $scripts = self::tryForKey( $this->languageScripts, $lang );
819  if ( $scripts ) {
820  return $scripts;
821  }
822  $fallbacks = MediaWikiServices::getInstance()->getLanguageFallback()
823  ->getAll( $lang, LanguageFallback::MESSAGES );
824  foreach ( $fallbacks as $lang ) {
825  $scripts = self::tryForKey( $this->languageScripts, $lang );
826  if ( $scripts ) {
827  return $scripts;
828  }
829  }
830 
831  return [];
832  }
833 
834  public function setSkinStylesOverride( array $moduleSkinStyles ): void {
835  $moduleName = $this->getName();
836  foreach ( $moduleSkinStyles as $skinName => $overrides ) {
837  // If a module provides overrides for a skin, and that skin also provides overrides
838  // for the same module, then the module has precedence.
839  if ( isset( $this->skinStyles[$skinName] ) ) {
840  continue;
841  }
842 
843  // If $moduleName in ResourceModuleSkinStyles is preceded with a '+', the defined style
844  // files will be added to 'default' skinStyles, otherwise 'default' will be ignored.
845  if ( isset( $overrides[$moduleName] ) ) {
846  $paths = (array)$overrides[$moduleName];
847  $styleFiles = [];
848  } elseif ( isset( $overrides['+' . $moduleName] ) ) {
849  $paths = (array)$overrides['+' . $moduleName];
850  $styleFiles = isset( $this->skinStyles['default'] ) ?
851  (array)$this->skinStyles['default'] :
852  [];
853  } else {
854  continue;
855  }
856 
857  // Add new file paths, remapping them to refer to our directories and not use settings
858  // from the module we're modifying, which come from the base definition.
859  list( $localBasePath, $remoteBasePath ) = self::extractBasePaths( $overrides );
860 
861  foreach ( $paths as $path ) {
863  }
864 
865  $this->skinStyles[$skinName] = $styleFiles;
866  }
867  }
868 
876  public function getStyleFiles( ResourceLoaderContext $context ) {
877  return array_merge_recursive(
878  self::collateFilePathListByOption( $this->styles, 'media', 'all' ),
879  self::collateFilePathListByOption(
880  self::tryForKey( $this->skinStyles, $context->getSkin(), 'default' ),
881  'media',
882  'all'
883  )
884  );
885  }
886 
894  protected function getSkinStyleFiles( $skinName ) {
896  self::tryForKey( $this->skinStyles, $skinName ),
897  'media',
898  'all'
899  );
900  }
901 
908  protected function getAllSkinStyleFiles() {
909  $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
910  $styleFiles = [];
911 
912  $internalSkinNames = array_keys( $skinFactory->getInstalledSkins() );
913  $internalSkinNames[] = 'default';
914 
915  foreach ( $internalSkinNames as $internalSkinName ) {
916  $styleFiles = array_merge_recursive(
917  $styleFiles,
918  $this->getSkinStyleFiles( $internalSkinName )
919  );
920  }
921 
922  return $styleFiles;
923  }
924 
930  public function getAllStyleFiles() {
931  $collatedStyleFiles = array_merge_recursive(
932  self::collateFilePathListByOption( $this->styles, 'media', 'all' ),
933  $this->getAllSkinStyleFiles()
934  );
935 
936  $result = [];
937 
938  foreach ( $collatedStyleFiles as $media => $styleFiles ) {
939  foreach ( $styleFiles as $styleFile ) {
940  $result[] = $this->getLocalPath( $styleFile );
941  }
942  }
943 
944  return $result;
945  }
946 
954  private function readScriptFiles( array $scripts ) {
955  if ( empty( $scripts ) ) {
956  return '';
957  }
958  $js = '';
959  foreach ( array_unique( $scripts, SORT_REGULAR ) as $fileName ) {
960  $localPath = $this->getLocalPath( $fileName );
961  $contents = $this->getFileContents( $localPath, 'script' );
963  }
964  return $js;
965  }
966 
977  public function readStyleFiles( array $styles, ResourceLoaderContext $context ) {
978  if ( !$styles ) {
979  return [];
980  }
981  foreach ( $styles as $media => $files ) {
982  $uniqueFiles = array_unique( $files, SORT_REGULAR );
983  $styleFiles = [];
984  foreach ( $uniqueFiles as $file ) {
985  $styleFiles[] = $this->readStyleFile( $file, $context );
986  }
987  $styles[$media] = implode( "\n", $styleFiles );
988  }
989  return $styles;
990  }
991 
1003  protected function readStyleFile( $path, ResourceLoaderContext $context ) {
1004  $localPath = $this->getLocalPath( $path );
1005  $style = $this->getFileContents( $localPath, 'style' );
1006  $styleLang = $this->getStyleSheetLang( $localPath );
1007 
1008  return $this->processStyle( $style, $styleLang, $path, $context );
1009  }
1010 
1027  protected function processStyle( $style, $styleLang, $path, ResourceLoaderContext $context ) {
1028  $localPath = $this->getLocalPath( $path );
1029  $remotePath = $this->getRemotePath( $path );
1030 
1031  if ( $styleLang === 'less' ) {
1032  $style = $this->compileLessString( $style, $localPath, $context );
1033  $this->hasGeneratedStyles = true;
1034  }
1035 
1036  if ( $this->getFlip( $context ) ) {
1037  $style = CSSJanus::transform(
1038  $style,
1039  /* $swapLtrRtlInURL = */ true,
1040  /* $swapLeftRightInURL = */ false
1041  );
1042  }
1043 
1044  $localDir = dirname( $localPath );
1045  $remoteDir = dirname( $remotePath );
1046  // Get and register local file references
1047  $localFileRefs = CSSMin::getLocalFileReferences( $style, $localDir );
1048  foreach ( $localFileRefs as $file ) {
1049  if ( file_exists( $file ) ) {
1050  $this->localFileRefs[] = $file;
1051  } else {
1052  $this->missingLocalFileRefs[] = $file;
1053  }
1054  }
1055  // Don't cache this call. remap() ensures data URIs embeds are up to date,
1056  // and urls contain correct content hashes in their query string. (T128668)
1057  return CSSMin::remap( $style, $localDir, $remoteDir, true );
1058  }
1059 
1065  public function getFlip( ResourceLoaderContext $context ) {
1066  return $context->getDirection() === 'rtl' && !$this->noflip;
1067  }
1068 
1074  public function getTargets() {
1075  return $this->targets;
1076  }
1077 
1084  public function getType() {
1085  $canBeStylesOnly = !(
1086  // All options except 'styles', 'skinStyles' and 'debugRaw'
1087  $this->scripts
1088  || $this->debugScripts
1089  || $this->templates
1090  || $this->languageScripts
1091  || $this->skinScripts
1092  || $this->dependencies
1093  || $this->messages
1094  || $this->skipFunction
1096  );
1097  return $canBeStylesOnly ? self::LOAD_STYLES : self::LOAD_GENERAL;
1098  }
1099 
1107  protected function compileLessFile( $fileName, ResourceLoaderContext $context ) {
1108  wfDeprecated( __METHOD__, '1.35' );
1109 
1110  $style = $this->getFileContents( $fileName, 'LESS' );
1111  return $this->compileLessString( $style, $fileName, $context );
1112  }
1113 
1126  protected function compileLessString( $style, $stylePath, ResourceLoaderContext $context ) {
1127  static $cache;
1128  // @TODO: dependency injection
1129  if ( !$cache ) {
1131  }
1132 
1133  $skinName = $context->getSkin();
1134  $skinImportPaths = ExtensionRegistry::getInstance()->getAttribute( 'SkinLessImportPaths' );
1135  $importDirs = [];
1136  if ( isset( $skinImportPaths[ $skinName ] ) ) {
1137  $importDirs[] = $skinImportPaths[ $skinName ];
1138  }
1139 
1140  $vars = $this->getLessVars( $context );
1141  // Construct a cache key from a hash of the LESS source, and a hash digest
1142  // of the LESS variables used for compilation.
1143  ksort( $vars );
1144  $compilerParams = [
1145  'vars' => $vars,
1146  'importDirs' => $importDirs,
1147  ];
1148  $key = $cache->makeGlobalKey(
1149  'resourceloader-less',
1150  'v1',
1151  hash( 'md4', $style ),
1152  hash( 'md4', serialize( $compilerParams ) )
1153  );
1154 
1155  // If we got a cached value, we have to validate it by getting a checksum of all the
1156  // files that were loaded by the parser and ensuring it matches the cached entry's.
1157  $data = $cache->get( $key );
1158  if (
1159  !$data ||
1160  $data['hash'] !== FileContentsHasher::getFileContentsHash( $data['files'] )
1161  ) {
1162  $compiler = $context->getResourceLoader()->getLessCompiler( $vars, $importDirs );
1163 
1164  $css = $compiler->parse( $style, $stylePath )->getCss();
1165  // T253055: store the implicit dependency paths in a form relative to any install
1166  // path so that multiple version of the application can share the cache for identical
1167  // less stylesheets. This also avoids churn during application updates.
1168  $files = $compiler->AllParsedFiles();
1169  $data = [
1170  'css' => $css,
1171  'files' => ResourceLoaderModule::getRelativePaths( $files ),
1172  'hash' => FileContentsHasher::getFileContentsHash( $files )
1173  ];
1174  $cache->set( $key, $data, $cache::TTL_DAY );
1175  }
1176 
1177  foreach ( ResourceLoaderModule::expandRelativePaths( $data['files'] ) as $path ) {
1178  $this->localFileRefs[] = $path;
1179  }
1180 
1181  return $data['css'];
1182  }
1183 
1189  public function getTemplates() {
1190  $templates = [];
1191 
1192  foreach ( $this->templates as $alias => $templatePath ) {
1193  // Alias is optional
1194  if ( is_int( $alias ) ) {
1195  $alias = $this->getPath( $templatePath );
1196  }
1197  $localPath = $this->getLocalPath( $templatePath );
1198  $content = $this->getFileContents( $localPath, 'template' );
1199 
1200  $templates[$alias] = $this->stripBom( $content );
1201  }
1202  return $templates;
1203  }
1204 
1224  private function expandPackageFiles( ResourceLoaderContext $context ) {
1225  $hash = $context->getHash();
1226  if ( isset( $this->expandedPackageFiles[$hash] ) ) {
1227  return $this->expandedPackageFiles[$hash];
1228  }
1229  if ( $this->packageFiles === null ) {
1230  return null;
1231  }
1232  $expandedFiles = [];
1233  $mainFile = null;
1234 
1235  foreach ( $this->packageFiles as $key => $fileInfo ) {
1236  if ( is_string( $fileInfo ) ) {
1237  $fileInfo = [ 'name' => $fileInfo, 'file' => $fileInfo ];
1238  }
1239  if ( !isset( $fileInfo['name'] ) ) {
1240  $msg = "Missing 'name' key in package file info for module '{$this->getName()}'," .
1241  " offset '{$key}'.";
1242  $this->getLogger()->error( $msg );
1243  throw new LogicException( $msg );
1244  }
1245  $fileName = $fileInfo['name'];
1246 
1247  // Infer type from alias if needed
1248  $type = $fileInfo['type'] ?? self::getPackageFileType( $fileName );
1249  $expanded = [ 'type' => $type ];
1250  if ( !empty( $fileInfo['main'] ) ) {
1251  $mainFile = $fileName;
1252  if ( $type !== 'script' && $type !== 'script-vue' ) {
1253  $msg = "Main file in package must be of type 'script', module " .
1254  "'{$this->getName()}', main file '{$mainFile}' is '{$type}'.";
1255  $this->getLogger()->error( $msg );
1256  throw new LogicException( $msg );
1257  }
1258  }
1259 
1260  // Perform expansions (except 'file' and 'callback'), creating one of these keys:
1261  // - 'content': literal value.
1262  // - 'filePath': content to be read from a file.
1263  // - 'callback': content computed by a callable.
1264  if ( isset( $fileInfo['content'] ) ) {
1265  $expanded['content'] = $fileInfo['content'];
1266  } elseif ( isset( $fileInfo['file'] ) ) {
1267  $expanded['filePath'] = $fileInfo['file'];
1268  } elseif ( isset( $fileInfo['callback'] ) ) {
1269  // If no extra parameter for the callback is given, use null.
1270  $expanded['callbackParam'] = $fileInfo['callbackParam'] ?? null;
1271 
1272  if ( !is_callable( $fileInfo['callback'] ) ) {
1273  $msg = "Invalid 'callback' for module '{$this->getName()}', file '{$fileName}'.";
1274  $this->getLogger()->error( $msg );
1275  throw new LogicException( $msg );
1276  }
1277  if ( isset( $fileInfo['versionCallback'] ) ) {
1278  if ( !is_callable( $fileInfo['versionCallback'] ) ) {
1279  throw new LogicException( "Invalid 'versionCallback' for "
1280  . "module '{$this->getName()}', file '{$fileName}'."
1281  );
1282  }
1283 
1284  // Execute the versionCallback with the same arguments that
1285  // would be given to the callback
1286  $callbackResult = ( $fileInfo['versionCallback'] )(
1287  $context,
1288  $this->getConfig(),
1289  $expanded['callbackParam']
1290  );
1291  if ( $callbackResult instanceof ResourceLoaderFilePath ) {
1292  $expanded['filePath'] = $callbackResult->getPath();
1293  } else {
1294  $expanded['definitionSummary'] = $callbackResult;
1295  }
1296  // Don't invoke 'callback' here as it may be expensive (T223260).
1297  $expanded['callback'] = $fileInfo['callback'];
1298  } else {
1299  // Else go ahead invoke callback with its arguments.
1300  $callbackResult = ( $fileInfo['callback'] )(
1301  $context,
1302  $this->getConfig(),
1303  $expanded['callbackParam']
1304  );
1305  if ( $callbackResult instanceof ResourceLoaderFilePath ) {
1306  $expanded['filePath'] = $callbackResult->getPath();
1307  } else {
1308  $expanded['content'] = $callbackResult;
1309  }
1310  }
1311  } elseif ( isset( $fileInfo['config'] ) ) {
1312  if ( $type !== 'data' ) {
1313  $msg = "Key 'config' only valid for data files. "
1314  . " Module '{$this->getName()}', file '{$fileName}' is '{$type}'.";
1315  $this->getLogger()->error( $msg );
1316  throw new LogicException( $msg );
1317  }
1318  $expandedConfig = [];
1319  foreach ( $fileInfo['config'] as $configKey => $var ) {
1320  $expandedConfig[ is_numeric( $configKey ) ? $var : $configKey ] = $this->getConfig()->get( $var );
1321  }
1322  $expanded['content'] = $expandedConfig;
1323  } elseif ( !empty( $fileInfo['main'] ) ) {
1324  // [ 'name' => 'foo.js', 'main' => true ] is shorthand
1325  $expanded['filePath'] = $fileName;
1326  } else {
1327  $msg = "Incomplete definition for module '{$this->getName()}', file '{$fileName}'. "
1328  . "One of 'file', 'content', 'callback', or 'config' must be set.";
1329  $this->getLogger()->error( $msg );
1330  throw new LogicException( $msg );
1331  }
1332 
1333  $expandedFiles[$fileName] = $expanded;
1334  }
1335 
1336  if ( $expandedFiles && $mainFile === null ) {
1337  // The first package file that is a script is the main file
1338  foreach ( $expandedFiles as $path => $file ) {
1339  if ( $file['type'] === 'script' || $file['type'] === 'script-vue' ) {
1340  $mainFile = $path;
1341  break;
1342  }
1343  }
1344  }
1345 
1346  $result = [
1347  'main' => $mainFile,
1348  'files' => $expandedFiles
1349  ];
1350 
1351  $this->expandedPackageFiles[$hash] = $result;
1352  return $result;
1353  }
1354 
1361  public function getPackageFiles( ResourceLoaderContext $context ) {
1362  if ( $this->packageFiles === null ) {
1363  return null;
1364  }
1365  $hash = $context->getHash();
1366  if ( isset( $this->fullyExpandedPackageFiles[ $hash ] ) ) {
1367  return $this->fullyExpandedPackageFiles[ $hash ];
1368  }
1369  $expandedPackageFiles = $this->expandPackageFiles( $context );
1370 
1371  // Expand file contents
1372  foreach ( $expandedPackageFiles['files'] as $fileName => &$fileInfo ) {
1373  // Turn any 'filePath' or 'callback' key into actual 'content',
1374  // and remove the key after that. The callback could return a
1375  // ResourceLoaderFilePath object; if that happens, fall through
1376  // to the 'filePath' handling.
1377  if ( isset( $fileInfo['callback'] ) ) {
1378  $callbackResult = ( $fileInfo['callback'] )(
1379  $context,
1380  $this->getConfig(),
1381  $fileInfo['callbackParam']
1382  );
1383  if ( $callbackResult instanceof ResourceLoaderFilePath ) {
1384  // Fall through to the filePath handling code below
1385  $fileInfo['filePath'] = $callbackResult->getPath();
1386  } else {
1387  $fileInfo['content'] = $callbackResult;
1388  }
1389  unset( $fileInfo['callback'] );
1390  }
1391  // Only interpret 'filePath' if 'content' hasn't been set already.
1392  // This can happen if 'versionCallback' provided 'filePath',
1393  // while 'callback' provides 'content'. In that case both are set
1394  // at this point. The 'filePath' from 'versionCallback' in that case is
1395  // only to inform getDefinitionSummary().
1396  if ( !isset( $fileInfo['content'] ) && isset( $fileInfo['filePath'] ) ) {
1397  $localPath = $this->getLocalPath( $fileInfo['filePath'] );
1398  $content = $this->getFileContents( $localPath, 'package' );
1399  if ( $fileInfo['type'] === 'data' ) {
1400  $content = json_decode( $content );
1401  }
1402  $fileInfo['content'] = $content;
1403  unset( $fileInfo['filePath'] );
1404  }
1405  if ( $fileInfo['type'] === 'script-vue' ) {
1406  try {
1407  $parsedComponent = $this->getVueComponentParser()->parse(
1408  $fileInfo['content'],
1409  [ 'minifyTemplate' => !$context->getDebug() ]
1410  );
1411  } catch ( Exception $e ) {
1412  $msg = "Error parsing file '$fileName' in module '{$this->getName()}': " .
1413  $e->getMessage();
1414  $this->getLogger()->error( $msg );
1415  throw new RuntimeException( $msg );
1416  }
1417  $encodedTemplate = json_encode( $parsedComponent['template'] );
1418  if ( $context->getDebug() ) {
1419  // Replace \n (backslash-n) with space + backslash-newline in debug mode
1420  // We only replace \n if not preceded by a backslash, to avoid breaking '\\n'
1421  $encodedTemplate = preg_replace( '/(?<!\\\\)\\\\n/', " \\\n", $encodedTemplate );
1422  // Expand \t to real tabs in debug mode
1423  $encodedTemplate = strtr( $encodedTemplate, [ "\\t" => "\t" ] );
1424  }
1425  $fileInfo['content'] = [
1426  'script' => $parsedComponent['script'] .
1427  ";\nmodule.exports.template = $encodedTemplate;",
1428  'style' => $parsedComponent['style'] ?? '',
1429  'styleLang' => $parsedComponent['styleLang'] ?? 'css'
1430  ];
1431  $fileInfo['type'] = 'script+style';
1432  }
1433 
1434  // Not needed for client response, exists for use by getDefinitionSummary().
1435  unset( $fileInfo['definitionSummary'] );
1436  // Not needed for client response, used by callbacks only.
1437  unset( $fileInfo['callbackParam'] );
1438  }
1439 
1440  $this->fullyExpandedPackageFiles[ $hash ] = $expandedPackageFiles;
1441  return $expandedPackageFiles;
1442  }
1443 
1454  protected function stripBom( $input ) {
1455  if ( substr_compare( "\xef\xbb\xbf", $input, 0, 3 ) === 0 ) {
1456  return substr( $input, 3 );
1457  }
1458  return $input;
1459  }
1460 }
ResourceLoaderFileModule\getSkipFunction
getSkipFunction()
Definition: ResourceLoaderFileModule.php:518
ResourceLoaderContext
Context object that contains information about the state of a specific ResourceLoader web request.
Definition: ResourceLoaderContext.php:34
ResourceLoaderFileModule\$styles
array $styles
List of paths to CSS files to always include.
Definition: ResourceLoaderFileModule.php:93
ResourceLoaderFileModule\$skinScripts
array $skinScripts
List of JavaScript files to include when using a specific skin.
Definition: ResourceLoaderFileModule.php:75
ResourceLoaderContext\getDirection
getDirection()
Definition: ResourceLoaderContext.php:201
ResourceLoaderFileModule\$debugScripts
array $debugScripts
List of paths to JavaScript files to include in debug mode.
Definition: ResourceLoaderFileModule.php:84
CACHE_ANYTHING
const CACHE_ANYTHING
Definition: Defines.php:85
ResourceLoader\ensureNewline
static ensureNewline( $str)
Ensure the string is either empty or ends in a line break.
Definition: ResourceLoader.php:1247
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:193
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
ResourceLoaderContext\getResourceLoader
getResourceLoader()
Definition: ResourceLoaderContext.php:150
ResourceLoaderFileModule\$templates
array $templates
Saves a list of the templates named by the modules.
Definition: ResourceLoaderFileModule.php:48
ResourceLoaderModule\$contents
array $contents
Map of (context hash => cached module content)
Definition: ResourceLoaderModule.php:66
ResourceLoaderFileModule\getStyles
getStyles(ResourceLoaderContext $context)
Get all styles for a given context.
Definition: ResourceLoaderFileModule.php:417
$fallback
$fallback
Definition: MessagesAb.php:11
ResourceLoaderFileModule\getFileHashes
getFileHashes(ResourceLoaderContext $context)
Helper method for getDefinitionSummary.
Definition: ResourceLoaderFileModule.php:548
ResourceLoaderFileModule\$noflip
bool $noflip
Whether CSSJanus flipping should be skipped for this module.
Definition: ResourceLoaderFileModule.php:158
ResourceLoaderFileModule\getScriptURLsForDebug
getScriptURLsForDebug(ResourceLoaderContext $context)
Definition: ResourceLoaderFileModule.php:387
ResourceLoaderFileModule\__construct
__construct(array $options=[], $localBasePath=null, $remoteBasePath=null)
Constructs a new module from an options array.
Definition: ResourceLoaderFileModule.php:200
ResourceLoaderFilePath
An object to represent a path to a JavaScript/CSS file, along with a remote and local base path,...
Definition: ResourceLoaderFilePath.php:28
MediaWiki\Languages\LanguageFallback
Definition: LanguageFallback.php:31
ResourceLoaderFileModule\enableModuleContentVersion
enableModuleContentVersion()
Disable module content versioning.
Definition: ResourceLoaderFileModule.php:538
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
ResourceLoaderFileModule\$debugRaw
bool $debugRaw
Link to raw files in debug mode.
Definition: ResourceLoaderFileModule.php:152
$wgExtensionAssetsPath
$wgExtensionAssetsPath
The URL path of the extensions directory.
Definition: DefaultSettings.php:243
ResourceLoaderFileModule\$hasGeneratedStyles
bool $hasGeneratedStyles
Whether getStyleURLsForDebug should return raw file paths, or return load.php urls.
Definition: ResourceLoaderFileModule.php:167
ResourceLoaderFileModule\$dependencies
array $dependencies
List of modules this module depends on.
Definition: ResourceLoaderFileModule.php:132
ResourceLoaderFileModule\$skinStyles
array $skinStyles
List of paths to CSS files to include when using specific skins.
Definition: ResourceLoaderFileModule.php:102
serialize
serialize()
Definition: ApiMessageTrait.php:138
ResourceLoaderFileModule\getFlip
getFlip(ResourceLoaderContext $context)
Get whether CSS for this module should be flipped.
Definition: ResourceLoaderFileModule.php:1065
ResourceLoaderModule\saveFileDependencies
saveFileDependencies(ResourceLoaderContext $context, array $curFileRefs)
Save the indirect dependencies for this module persuant to the skin/language context.
Definition: ResourceLoaderModule.php:538
ResourceLoaderFileModule\$targets
string[] $targets
Definition: ResourceLoaderFileModule.php:155
$wgStylePath
$wgStylePath
The URL path of the skins directory.
Definition: DefaultSettings.php:228
ResourceLoaderFileModule\getDefinitionSummary
getDefinitionSummary(ResourceLoaderContext $context)
Get the definition summary for this module.
Definition: ResourceLoaderFileModule.php:608
FileContentsHasher\getFileContentsHash
static getFileContentsHash( $filePaths, $algo='md4')
Get a hash of the combined contents of one or more files, either by retrieving a previously-computed ...
Definition: FileContentsHasher.php:88
ResourceLoaderModule\getLessVars
getLessVars(ResourceLoaderContext $context)
Get module-specific LESS variables, if any.
Definition: ResourceLoaderModule.php:725
ResourceLoaderFileModule
Module based on local JavaScript/CSS files.
Definition: ResourceLoaderFileModule.php:40
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:134
ResourceLoaderFileModule\$es6
bool $es6
Whether this module requires the client to support ES6.
Definition: ResourceLoaderFileModule.php:161
ResourceLoaderFileModule\getGroup
getGroup()
Gets the name of the group this module should be loaded in.
Definition: ResourceLoaderFileModule.php:484
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Definition: GlobalFunctions.php:997
ResourceLoaderModule\getLogger
getLogger()
Definition: ResourceLoaderModule.php:262
ResourceLoaderFileModule\getSkinStyleFiles
getSkinStyleFiles( $skinName)
Gets a list of file paths for all skin styles in the module used by the skin.
Definition: ResourceLoaderFileModule.php:894
ResourceLoaderFileModule\compileLessFile
compileLessFile( $fileName, ResourceLoaderContext $context)
Definition: ResourceLoaderFileModule.php:1107
Config\get
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
ResourceLoaderFileModule\getAllStyleFiles
getAllStyleFiles()
Returns all style files and all skin style files used by this module.
Definition: ResourceLoaderFileModule.php:930
ResourceLoaderContext\getDebug
getDebug()
Definition: ResourceLoaderContext.php:265
ResourceLoaderFileModule\processStyle
processStyle( $style, $styleLang, $path, ResourceLoaderContext $context)
Process a CSS/LESS string.
Definition: ResourceLoaderFileModule.php:1027
ResourceLoaderFileModule\getStyleURLsForDebug
getStyleURLsForDebug(ResourceLoaderContext $context)
Definition: ResourceLoaderFileModule.php:449
ResourceLoaderFileModule\getRemotePath
getRemotePath( $path)
Definition: ResourceLoaderFileModule.php:702
ResourceLoaderModule\getDeprecationInformation
getDeprecationInformation(ResourceLoaderContext $context)
Get JS representing deprecation information for the current module if available.
Definition: ResourceLoaderModule.php:180
ResourceLoaderFileModule\expandPackageFiles
expandPackageFiles(ResourceLoaderContext $context)
Internal helper for use by getPackageFiles(), getFileHashes() and getDefinitionSummary().
Definition: ResourceLoaderFileModule.php:1224
ResourceLoaderModule\expandRelativePaths
static expandRelativePaths(array $filePaths)
Expand directories relative to $IP.
Definition: ResourceLoaderModule.php:595
ResourceLoaderContext\getLanguage
getLanguage()
Definition: ResourceLoaderContext.php:183
ResourceLoaderFileModule\$messages
array $messages
List of message keys used by this module.
Definition: ResourceLoaderFileModule.php:146
ResourceLoaderModule\getMessageBlob
getMessageBlob(ResourceLoaderContext $context)
Get the hash of the message blob.
Definition: ResourceLoaderModule.php:610
ResourceLoaderFileModule\$languageScripts
array $languageScripts
List of JavaScript files to include when using a specific language.
Definition: ResourceLoaderFileModule.php:66
ResourceLoaderFileModule\stripBom
stripBom( $input)
Takes an input string and removes the UTF-8 BOM character if present.
Definition: ResourceLoaderFileModule.php:1454
ResourceLoaderFileModule\extractBasePaths
static extractBasePaths(array $options=[], $localBasePath=null, $remoteBasePath=null)
Extract a pair of local and remote base paths from module definition information.
Definition: ResourceLoaderFileModule.php:306
ResourceLoaderFileModule\getPackageFiles
getPackageFiles(ResourceLoaderContext $context)
Resolves the package files definition and generates the content of each package file.
Definition: ResourceLoaderFileModule.php:1361
ResourceLoaderFileModule\setSkinStylesOverride
setSkinStylesOverride(array $moduleSkinStyles)
Provide overrides for skinStyles to modules that support that.
Definition: ResourceLoaderFileModule.php:834
$content
$content
Definition: router.php:76
ResourceLoaderFileModule\getDependencies
getDependencies(ResourceLoaderContext $context=null)
Gets list of names of modules this module depends on.
Definition: ResourceLoaderFileModule.php:493
ResourceLoaderFileModule\getTargets
getTargets()
Get target(s) for the module, eg ['desktop'] or ['desktop', 'mobile'].
Definition: ResourceLoaderFileModule.php:1074
ResourceLoaderFileModule\$skipFunction
string $skipFunction
File name containing the body of the skip function.
Definition: ResourceLoaderFileModule.php:137
ResourceLoaderFileModule\$group
string $group
Name of group to load this module in.
Definition: ResourceLoaderFileModule.php:149
ResourceLoaderFileModule\$localFileRefs
array $localFileRefs
Place where readStyleFile() tracks file dependencies.
Definition: ResourceLoaderFileModule.php:176
ResourceLoaderFileModule\readScriptFiles
readScriptFiles(array $scripts)
Get the contents of a list of JavaScript files.
Definition: ResourceLoaderFileModule.php:954
ResourceLoaderFileModule\getPath
getPath( $path)
Definition: ResourceLoaderFileModule.php:678
ResourceLoaderFileModule\collateFilePathListByOption
static collateFilePathListByOption(array $list, $option, $default)
Collates file paths by option (where provided).
Definition: ResourceLoaderFileModule.php:749
ResourceLoaderContext\getSkin
getSkin()
Definition: ResourceLoaderContext.php:215
VueComponentParser
Parser for Vue single file components (.vue files).
Definition: VueComponentParser.php:39
ResourceLoaderFileModule\$expandedPackageFiles
array $expandedPackageFiles
Expanded versions of $packageFiles, lazy-computed by expandPackageFiles(); keyed by context hash.
Definition: ResourceLoaderFileModule.php:117
ResourceLoaderFileModule\$localBasePath
string $localBasePath
Local base path, see __construct()
Definition: ResourceLoaderFileModule.php:42
$wgResourceBasePath
$wgResourceBasePath
The default 'remoteBasePath' value for instances of ResourceLoaderFileModule.
Definition: DefaultSettings.php:4411
ResourceLoaderFileModule\getScriptFiles
getScriptFiles(ResourceLoaderContext $context)
Get a list of script file paths for this module, in order of proper execution.
Definition: ResourceLoaderFileModule.php:797
ResourceLoaderFileModule\getStyleSheetLang
getStyleSheetLang( $path)
Infer the stylesheet language from a stylesheet file path.
Definition: ResourceLoaderFileModule.php:721
ResourceLoaderFileModule\getLocalPath
getLocalPath( $path)
Definition: ResourceLoaderFileModule.php:690
ResourceLoaderModule
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
Definition: ResourceLoaderModule.php:39
ResourceLoaderModule\$config
Config $config
Definition: ResourceLoaderModule.php:41
$cache
$cache
Definition: mcc.php:33
ResourceLoaderFileModule\tryForKey
static tryForKey(array $list, $key, $fallback=null)
Get a list of element that match a key, optionally using a fallback key.
Definition: ResourceLoaderFileModule.php:779
ResourceLoaderFileModule\readStyleFile
readStyleFile( $path, ResourceLoaderContext $context)
Read and process a style file.
Definition: ResourceLoaderFileModule.php:1003
ResourceLoaderFileModule\getTemplates
getTemplates()
Takes named templates by the module and returns an array mapping.
Definition: ResourceLoaderFileModule.php:1189
ResourceLoaderFileModule\$fullyExpandedPackageFiles
array $fullyExpandedPackageFiles
Further expanded versions of $expandedPackageFiles, lazy-computed by getPackageFiles(); keyed by cont...
Definition: ResourceLoaderFileModule.php:123
$path
$path
Definition: NoLocalSettings.php:25
ResourceLoaderFileModule\readStyleFiles
readStyleFiles(array $styles, ResourceLoaderContext $context)
Get the contents of a list of CSS files.
Definition: ResourceLoaderFileModule.php:977
ResourceLoaderModule\getRelativePaths
static getRelativePaths(array $filePaths)
Make file paths relative to MediaWiki directory.
Definition: ResourceLoaderModule.php:581
ResourceLoaderModule\getFileDependencies
getFileDependencies(ResourceLoaderContext $context)
Get the indirect dependencies for this module persuant to the skin/language context.
Definition: ResourceLoaderModule.php:499
ResourceLoaderFileModule\$remoteBasePath
string $remoteBasePath
Remote base path, see __construct()
Definition: ResourceLoaderFileModule.php:45
ResourceLoaderFileModule\supportsURLLoading
supportsURLLoading()
Definition: ResourceLoaderFileModule.php:405
ResourceLoaderContext\getHash
getHash()
All factors that uniquely identify this request, except 'modules'.
Definition: ResourceLoaderContext.php:384
ResourceLoaderFileModule\getFileContents
getFileContents( $localPath, $type)
Helper method for getting a file.
Definition: ResourceLoaderFileModule.php:505
ResourceLoaderFileModule\getMessages
getMessages()
Gets list of message keys used by this module.
Definition: ResourceLoaderFileModule.php:475
ResourceLoaderFileModule\getVueComponentParser
getVueComponentParser()
Definition: ResourceLoaderFileModule.php:667
$IP
$IP
Definition: WebStart.php:49
ResourceLoaderFileModule\getAllSkinStyleFiles
getAllSkinStyleFiles()
Gets a list of file paths for all skin style files in the module, for all available skins.
Definition: ResourceLoaderFileModule.php:908
ResourceLoaderFileModule\compileLessString
compileLessString( $style, $stylePath, ResourceLoaderContext $context)
Compile a LESS string into CSS.
Definition: ResourceLoaderFileModule.php:1126
ResourceLoaderFileModule\getStyleFiles
getStyleFiles(ResourceLoaderContext $context)
Get a list of file paths for all styles in this module, in order of proper inclusion.
Definition: ResourceLoaderFileModule.php:876
ResourceLoaderFileModule\getType
getType()
Get the module's load type.
Definition: ResourceLoaderFileModule.php:1084
ResourceLoaderFileModule\getScript
getScript(ResourceLoaderContext $context)
Gets all scripts for a given context concatenated together.
Definition: ResourceLoaderFileModule.php:362
ResourceLoaderFileModule\$scripts
array $scripts
List of paths to JavaScript files to always include.
Definition: ResourceLoaderFileModule.php:57
ResourceLoaderModule\getName
getName()
Get this module's name.
Definition: ResourceLoaderModule.php:115
ResourceLoaderFileModule\getLanguageScripts
getLanguageScripts( $lang)
Get the set of language scripts for the given language, possibly using a fallback language.
Definition: ResourceLoaderFileModule.php:817
ResourceLoaderModule\getConfig
getConfig()
Definition: ResourceLoaderModule.php:234
ResourceLoaderFileModule\$missingLocalFileRefs
array $missingLocalFileRefs
Place where readStyleFile() tracks file dependencies for non-existent files.
Definition: ResourceLoaderFileModule.php:182
OutputPage\transformResourcePath
static transformResourcePath(Config $config, $path)
Transform path to web-accessible static resource.
Definition: OutputPage.php:3973
ResourceLoaderFileModule\$packageFiles
array $packageFiles
List of packaged files to make available through require()
Definition: ResourceLoaderFileModule.php:111
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:255
ResourceLoaderFileModule\getPackageFileType
static getPackageFileType( $path)
Infer the file type from a package file path.
Definition: ResourceLoaderFileModule.php:730
ResourceLoaderFileModule\$vueComponentParser
VueComponentParser null $vueComponentParser
Lazy-created by getVueComponentParser()
Definition: ResourceLoaderFileModule.php:187
ResourceLoaderFileModule\requiresES6
requiresES6()
Whether the module requires ES6 support in the client.
Definition: ResourceLoaderFileModule.php:526
$type
$type
Definition: testCompression.php:52