MediaWiki  1.29.2
ResourceLoader.php
Go to the documentation of this file.
1 <?php
26 use Psr\Log\LoggerAwareInterface;
27 use Psr\Log\LoggerInterface;
28 use Psr\Log\NullLogger;
29 use WrappedString\WrappedString;
31 
38 class ResourceLoader implements LoggerAwareInterface {
40  protected static $filterCacheVersion = 7;
41 
43  protected static $debugMode = null;
44 
46  private $lessVars = null;
47 
52  protected $modules = [];
53 
58  protected $moduleInfos = [];
59 
61  protected $config;
62 
68  protected $testModuleNames = [];
69 
74  protected $sources = [];
75 
80  protected $errors = [];
81 
85  protected $blobStore;
86 
90  private $logger;
91 
93  const FILTER_NOMIN = '/*@nomin*/';
94 
109  public function preloadModuleInfo( array $moduleNames, ResourceLoaderContext $context ) {
110  if ( !$moduleNames ) {
111  // Or else Database*::select() will explode, plus it's cheaper!
112  return;
113  }
114  $dbr = wfGetDB( DB_REPLICA );
115  $skin = $context->getSkin();
117 
118  // Batched version of ResourceLoaderModule::getFileDependencies
119  $vary = "$skin|$lang";
120  $res = $dbr->select( 'module_deps', [ 'md_module', 'md_deps' ], [
121  'md_module' => $moduleNames,
122  'md_skin' => $vary,
123  ], __METHOD__
124  );
125 
126  // Prime in-object cache for file dependencies
127  $modulesWithDeps = [];
128  foreach ( $res as $row ) {
129  $module = $this->getModule( $row->md_module );
130  if ( $module ) {
131  $module->setFileDependencies( $context, ResourceLoaderModule::expandRelativePaths(
132  FormatJson::decode( $row->md_deps, true )
133  ) );
134  $modulesWithDeps[] = $row->md_module;
135  }
136  }
137  // Register the absence of a dependency row too
138  foreach ( array_diff( $moduleNames, $modulesWithDeps ) as $name ) {
139  $module = $this->getModule( $name );
140  if ( $module ) {
141  $this->getModule( $name )->setFileDependencies( $context, [] );
142  }
143  }
144 
145  // Batched version of ResourceLoaderWikiModule::getTitleInfo
147 
148  // Prime in-object cache for message blobs for modules with messages
149  $modules = [];
150  foreach ( $moduleNames as $name ) {
151  $module = $this->getModule( $name );
152  if ( $module && $module->getMessages() ) {
153  $modules[$name] = $module;
154  }
155  }
156  $store = $this->getMessageBlobStore();
157  $blobs = $store->getBlobs( $modules, $lang );
158  foreach ( $blobs as $name => $blob ) {
159  $modules[$name]->setMessageBlob( $blob, $lang );
160  }
161  }
162 
180  public static function filter( $filter, $data, array $options = [] ) {
181  if ( strpos( $data, ResourceLoader::FILTER_NOMIN ) !== false ) {
182  return $data;
183  }
184 
185  if ( isset( $options['cache'] ) && $options['cache'] === false ) {
186  return self::applyFilter( $filter, $data );
187  }
188 
189  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
191 
192  $key = $cache->makeGlobalKey(
193  'resourceloader',
194  'filter',
195  $filter,
196  self::$filterCacheVersion, md5( $data )
197  );
198 
199  $result = $cache->get( $key );
200  if ( $result === false ) {
201  $stats->increment( "resourceloader_cache.$filter.miss" );
202  $result = self::applyFilter( $filter, $data );
203  $cache->set( $key, $result, 24 * 3600 );
204  } else {
205  $stats->increment( "resourceloader_cache.$filter.hit" );
206  }
207  if ( $result === null ) {
208  // Cached failure
209  $result = $data;
210  }
211 
212  return $result;
213  }
214 
215  private static function applyFilter( $filter, $data ) {
216  $data = trim( $data );
217  if ( $data ) {
218  try {
219  $data = ( $filter === 'minify-css' )
220  ? CSSMin::minify( $data )
221  : JavaScriptMinifier::minify( $data );
222  } catch ( Exception $e ) {
224  return null;
225  }
226  }
227  return $data;
228  }
229 
230  /* Methods */
231 
237  public function __construct( Config $config = null, LoggerInterface $logger = null ) {
238  global $IP;
239 
240  $this->logger = $logger ?: new NullLogger();
241 
242  if ( !$config ) {
243  $this->logger->debug( __METHOD__ . ' was called without providing a Config instance' );
244  $config = MediaWikiServices::getInstance()->getMainConfig();
245  }
246  $this->config = $config;
247 
248  // Add 'local' source first
249  $this->addSource( 'local', $config->get( 'LoadScript' ) );
250 
251  // Add other sources
252  $this->addSource( $config->get( 'ResourceLoaderSources' ) );
253 
254  // Register core modules
255  $this->register( include "$IP/resources/Resources.php" );
256  $this->register( include "$IP/resources/ResourcesOOUI.php" );
257  // Register extension modules
258  $this->register( $config->get( 'ResourceModules' ) );
259 
260  // Avoid PHP 7.1 warning from passing $this by reference
261  $rl = $this;
262  Hooks::run( 'ResourceLoaderRegisterModules', [ &$rl ] );
263 
264  if ( $config->get( 'EnableJavaScriptTest' ) === true ) {
265  $this->registerTestModules();
266  }
267 
268  $this->setMessageBlobStore( new MessageBlobStore( $this, $this->logger ) );
269  }
270 
274  public function getConfig() {
275  return $this->config;
276  }
277 
282  public function setLogger( LoggerInterface $logger ) {
283  $this->logger = $logger;
284  }
285 
290  public function getLogger() {
291  return $this->logger;
292  }
293 
298  public function getMessageBlobStore() {
299  return $this->blobStore;
300  }
301 
306  public function setMessageBlobStore( MessageBlobStore $blobStore ) {
307  $this->blobStore = $blobStore;
308  }
309 
323  public function register( $name, $info = null ) {
324  $moduleSkinStyles = $this->config->get( 'ResourceModuleSkinStyles' );
325 
326  // Allow multiple modules to be registered in one call
327  $registrations = is_array( $name ) ? $name : [ $name => $info ];
328  foreach ( $registrations as $name => $info ) {
329  // Warn on duplicate registrations
330  if ( isset( $this->moduleInfos[$name] ) ) {
331  // A module has already been registered by this name
332  $this->logger->warning(
333  'ResourceLoader duplicate registration warning. ' .
334  'Another module has already been registered as ' . $name
335  );
336  }
337 
338  // Check $name for validity
339  if ( !self::isValidModuleName( $name ) ) {
340  throw new MWException( "ResourceLoader module name '$name' is invalid, "
341  . "see ResourceLoader::isValidModuleName()" );
342  }
343 
344  // Attach module
345  if ( $info instanceof ResourceLoaderModule ) {
346  $this->moduleInfos[$name] = [ 'object' => $info ];
347  $info->setName( $name );
348  $this->modules[$name] = $info;
349  } elseif ( is_array( $info ) ) {
350  // New calling convention
351  $this->moduleInfos[$name] = $info;
352  } else {
353  throw new MWException(
354  'ResourceLoader module info type error for module \'' . $name .
355  '\': expected ResourceLoaderModule or array (got: ' . gettype( $info ) . ')'
356  );
357  }
358 
359  // Last-minute changes
360 
361  // Apply custom skin-defined styles to existing modules.
362  if ( $this->isFileModule( $name ) ) {
363  foreach ( $moduleSkinStyles as $skinName => $skinStyles ) {
364  // If this module already defines skinStyles for this skin, ignore $wgResourceModuleSkinStyles.
365  if ( isset( $this->moduleInfos[$name]['skinStyles'][$skinName] ) ) {
366  continue;
367  }
368 
369  // If $name is preceded with a '+', the defined style files will be added to 'default'
370  // skinStyles, otherwise 'default' will be ignored as it normally would be.
371  if ( isset( $skinStyles[$name] ) ) {
372  $paths = (array)$skinStyles[$name];
373  $styleFiles = [];
374  } elseif ( isset( $skinStyles['+' . $name] ) ) {
375  $paths = (array)$skinStyles['+' . $name];
376  $styleFiles = isset( $this->moduleInfos[$name]['skinStyles']['default'] ) ?
377  (array)$this->moduleInfos[$name]['skinStyles']['default'] :
378  [];
379  } else {
380  continue;
381  }
382 
383  // Add new file paths, remapping them to refer to our directories and not use settings
384  // from the module we're modifying, which come from the base definition.
385  list( $localBasePath, $remoteBasePath ) =
387 
388  foreach ( $paths as $path ) {
389  $styleFiles[] = new ResourceLoaderFilePath( $path, $localBasePath, $remoteBasePath );
390  }
391 
392  $this->moduleInfos[$name]['skinStyles'][$skinName] = $styleFiles;
393  }
394  }
395  }
396  }
397 
398  public function registerTestModules() {
399  global $IP;
400 
401  if ( $this->config->get( 'EnableJavaScriptTest' ) !== true ) {
402  throw new MWException( 'Attempt to register JavaScript test modules '
403  . 'but <code>$wgEnableJavaScriptTest</code> is false. '
404  . 'Edit your <code>LocalSettings.php</code> to enable it.' );
405  }
406 
407  // Get core test suites
408  $testModules = [];
409  $testModules['qunit'] = [];
410  // Get other test suites (e.g. from extensions)
411  // Avoid PHP 7.1 warning from passing $this by reference
412  $rl = $this;
413  Hooks::run( 'ResourceLoaderTestModules', [ &$testModules, &$rl ] );
414 
415  // Add the testrunner (which configures QUnit) to the dependencies.
416  // Since it must be ready before any of the test suites are executed.
417  foreach ( $testModules['qunit'] as &$module ) {
418  // Make sure all test modules are top-loading so that when QUnit starts
419  // on document-ready, it will run once and finish. If some tests arrive
420  // later (possibly after QUnit has already finished) they will be ignored.
421  $module['position'] = 'top';
422  $module['dependencies'][] = 'test.mediawiki.qunit.testrunner';
423  }
424 
425  $testModules['qunit'] =
426  ( include "$IP/tests/qunit/QUnitTestResources.php" ) + $testModules['qunit'];
427 
428  foreach ( $testModules as $id => $names ) {
429  // Register test modules
430  $this->register( $testModules[$id] );
431 
432  // Keep track of their names so that they can be loaded together
433  $this->testModuleNames[$id] = array_keys( $testModules[$id] );
434  }
435  }
436 
447  public function addSource( $id, $loadUrl = null ) {
448  // Allow multiple sources to be registered in one call
449  if ( is_array( $id ) ) {
450  foreach ( $id as $key => $value ) {
451  $this->addSource( $key, $value );
452  }
453  return;
454  }
455 
456  // Disallow duplicates
457  if ( isset( $this->sources[$id] ) ) {
458  throw new MWException(
459  'ResourceLoader duplicate source addition error. ' .
460  'Another source has already been registered as ' . $id
461  );
462  }
463 
464  // Pre 1.24 backwards-compatibility
465  if ( is_array( $loadUrl ) ) {
466  if ( !isset( $loadUrl['loadScript'] ) ) {
467  throw new MWException(
468  __METHOD__ . ' was passed an array with no "loadScript" key.'
469  );
470  }
471 
472  $loadUrl = $loadUrl['loadScript'];
473  }
474 
475  $this->sources[$id] = $loadUrl;
476  }
477 
483  public function getModuleNames() {
484  return array_keys( $this->moduleInfos );
485  }
486 
497  public function getTestModuleNames( $framework = 'all' ) {
499  if ( $framework == 'all' ) {
500  return $this->testModuleNames;
501  } elseif ( isset( $this->testModuleNames[$framework] )
502  && is_array( $this->testModuleNames[$framework] )
503  ) {
504  return $this->testModuleNames[$framework];
505  } else {
506  return [];
507  }
508  }
509 
517  public function isModuleRegistered( $name ) {
518  return isset( $this->moduleInfos[$name] );
519  }
520 
532  public function getModule( $name ) {
533  if ( !isset( $this->modules[$name] ) ) {
534  if ( !isset( $this->moduleInfos[$name] ) ) {
535  // No such module
536  return null;
537  }
538  // Construct the requested object
539  $info = $this->moduleInfos[$name];
541  if ( isset( $info['object'] ) ) {
542  // Object given in info array
543  $object = $info['object'];
544  } else {
545  if ( !isset( $info['class'] ) ) {
546  $class = 'ResourceLoaderFileModule';
547  } else {
548  $class = $info['class'];
549  }
551  $object = new $class( $info );
552  $object->setConfig( $this->getConfig() );
553  $object->setLogger( $this->logger );
554  }
555  $object->setName( $name );
556  $this->modules[$name] = $object;
557  }
558 
559  return $this->modules[$name];
560  }
561 
568  protected function isFileModule( $name ) {
569  if ( !isset( $this->moduleInfos[$name] ) ) {
570  return false;
571  }
572  $info = $this->moduleInfos[$name];
573  if ( isset( $info['object'] ) || isset( $info['class'] ) ) {
574  return false;
575  }
576  return true;
577  }
578 
584  public function getSources() {
585  return $this->sources;
586  }
587 
597  public function getLoadScript( $source ) {
598  if ( !isset( $this->sources[$source] ) ) {
599  throw new MWException( "The $source source was never registered in ResourceLoader." );
600  }
601  return $this->sources[$source];
602  }
603 
609  public static function makeHash( $value ) {
610  $hash = hash( 'fnv132', $value );
611  return Wikimedia\base_convert( $hash, 16, 36, 7 );
612  }
613 
624  protected function outputErrorAndLog( Exception $e, $msg, array $context = [] ) {
626  $this->logger->warning(
627  $msg,
628  $context + [ 'exception' => $e ]
629  );
630  $this->errors[] = self::formatExceptionNoComment( $e );
631  }
632 
641  public function getCombinedVersion( ResourceLoaderContext $context, array $moduleNames ) {
642  if ( !$moduleNames ) {
643  return '';
644  }
645  $hashes = array_map( function ( $module ) use ( $context ) {
646  try {
647  return $this->getModule( $module )->getVersionHash( $context );
648  } catch ( Exception $e ) {
649  // If modules fail to compute a version, do still consider the versions
650  // of other modules - don't set an empty string E-Tag for the whole request.
651  // See also T152266 and StartupModule::getModuleRegistrations().
652  $this->outputErrorAndLog( $e,
653  'Calculating version for "{module}" failed: {exception}',
654  [
655  'module' => $module,
656  ]
657  );
658  return '';
659  }
660  }, $moduleNames );
661  return self::makeHash( implode( '', $hashes ) );
662  }
663 
678  public function makeVersionQuery( ResourceLoaderContext $context ) {
679  // As of MediaWiki 1.28, the server and client use the same algorithm for combining
680  // version hashes. There is no technical reason for this to be same, and for years the
681  // implementations differed. If getCombinedVersion in PHP (used for StartupModule and
682  // E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
683  // query parameter), then this method must continue to match the JS one.
684  $moduleNames = [];
685  foreach ( $context->getModules() as $name ) {
686  if ( !$this->getModule( $name ) ) {
687  // If a versioned request contains a missing module, the version is a mismatch
688  // as the client considered a module (and version) we don't have.
689  return '';
690  }
691  $moduleNames[] = $name;
692  }
693  return $this->getCombinedVersion( $context, $moduleNames );
694  }
695 
701  public function respond( ResourceLoaderContext $context ) {
702  // Buffer output to catch warnings. Normally we'd use ob_clean() on the
703  // top-level output buffer to clear warnings, but that breaks when ob_gzhandler
704  // is used: ob_clean() will clear the GZIP header in that case and it won't come
705  // back for subsequent output, resulting in invalid GZIP. So we have to wrap
706  // the whole thing in our own output buffer to be sure the active buffer
707  // doesn't use ob_gzhandler.
708  // See https://bugs.php.net/bug.php?id=36514
709  ob_start();
710 
711  // Find out which modules are missing and instantiate the others
712  $modules = [];
713  $missing = [];
714  foreach ( $context->getModules() as $name ) {
715  $module = $this->getModule( $name );
716  if ( $module ) {
717  // Do not allow private modules to be loaded from the web.
718  // This is a security issue, see T36907.
719  if ( $module->getGroup() === 'private' ) {
720  $this->logger->debug( "Request for private module '$name' denied" );
721  $this->errors[] = "Cannot show private module \"$name\"";
722  continue;
723  }
724  $modules[$name] = $module;
725  } else {
726  $missing[] = $name;
727  }
728  }
729 
730  try {
731  // Preload for getCombinedVersion() and for batch makeModuleResponse()
732  $this->preloadModuleInfo( array_keys( $modules ), $context );
733  } catch ( Exception $e ) {
734  $this->outputErrorAndLog( $e, 'Preloading module info failed: {exception}' );
735  }
736 
737  // Combine versions to propagate cache invalidation
738  $versionHash = '';
739  try {
740  $versionHash = $this->getCombinedVersion( $context, array_keys( $modules ) );
741  } catch ( Exception $e ) {
742  $this->outputErrorAndLog( $e, 'Calculating version hash failed: {exception}' );
743  }
744 
745  // See RFC 2616 § 3.11 Entity Tags
746  // https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11
747  $etag = 'W/"' . $versionHash . '"';
748 
749  // Try the client-side cache first
750  if ( $this->tryRespondNotModified( $context, $etag ) ) {
751  return; // output handled (buffers cleared)
752  }
753 
754  // Use file cache if enabled and available...
755  if ( $this->config->get( 'UseFileCache' ) ) {
757  if ( $this->tryRespondFromFileCache( $fileCache, $context, $etag ) ) {
758  return; // output handled
759  }
760  }
761 
762  // Generate a response
763  $response = $this->makeModuleResponse( $context, $modules, $missing );
764 
765  // Capture any PHP warnings from the output buffer and append them to the
766  // error list if we're in debug mode.
767  if ( $context->getDebug() ) {
768  $warnings = ob_get_contents();
769  if ( strlen( $warnings ) ) {
770  $this->errors[] = $warnings;
771  }
772  }
773 
774  // Save response to file cache unless there are errors
775  if ( isset( $fileCache ) && !$this->errors && !count( $missing ) ) {
776  // Cache single modules and images...and other requests if there are enough hits
778  if ( $fileCache->isCacheWorthy() ) {
779  $fileCache->saveText( $response );
780  } else {
781  $fileCache->incrMissesRecent( $context->getRequest() );
782  }
783  }
784  }
785 
786  $this->sendResponseHeaders( $context, $etag, (bool)$this->errors );
787 
788  // Remove the output buffer and output the response
789  ob_end_clean();
790 
791  if ( $context->getImageObj() && $this->errors ) {
792  // We can't show both the error messages and the response when it's an image.
793  $response = implode( "\n\n", $this->errors );
794  } elseif ( $this->errors ) {
795  $errorText = implode( "\n\n", $this->errors );
796  $errorResponse = self::makeComment( $errorText );
797  if ( $context->shouldIncludeScripts() ) {
798  $errorResponse .= 'if (window.console && console.error) {'
799  . Xml::encodeJsCall( 'console.error', [ $errorText ] )
800  . "}\n";
801  }
802 
803  // Prepend error info to the response
804  $response = $errorResponse . $response;
805  }
806 
807  $this->errors = [];
808  echo $response;
809  }
810 
821  protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
823  $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
824  // Use a short cache expiry so that updates propagate to clients quickly, if:
825  // - No version specified (shared resources, e.g. stylesheets)
826  // - There were errors (recover quickly)
827  // - Version mismatch (T117587, T47877)
828  if ( is_null( $context->getVersion() )
829  || $errors
830  || $context->getVersion() !== $this->makeVersionQuery( $context )
831  ) {
832  $maxage = $rlMaxage['unversioned']['client'];
833  $smaxage = $rlMaxage['unversioned']['server'];
834  // If a version was specified we can use a longer expiry time since changing
835  // version numbers causes cache misses
836  } else {
837  $maxage = $rlMaxage['versioned']['client'];
838  $smaxage = $rlMaxage['versioned']['server'];
839  }
840  if ( $context->getImageObj() ) {
841  // Output different headers if we're outputting textual errors.
842  if ( $errors ) {
843  header( 'Content-Type: text/plain; charset=utf-8' );
844  } else {
845  $context->getImageObj()->sendResponseHeaders( $context );
846  }
847  } elseif ( $context->getOnly() === 'styles' ) {
848  header( 'Content-Type: text/css; charset=utf-8' );
849  header( 'Access-Control-Allow-Origin: *' );
850  } else {
851  header( 'Content-Type: text/javascript; charset=utf-8' );
852  }
853  // See RFC 2616 § 14.19 ETag
854  // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19
855  header( 'ETag: ' . $etag );
856  if ( $context->getDebug() ) {
857  // Do not cache debug responses
858  header( 'Cache-Control: private, no-cache, must-revalidate' );
859  header( 'Pragma: no-cache' );
860  } else {
861  header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
862  $exp = min( $maxage, $smaxage );
863  header( 'Expires: ' . wfTimestamp( TS_RFC2822, $exp + time() ) );
864  }
865  }
866 
877  protected function tryRespondNotModified( ResourceLoaderContext $context, $etag ) {
878  // See RFC 2616 § 14.26 If-None-Match
879  // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
880  $clientKeys = $context->getRequest()->getHeader( 'If-None-Match', WebRequest::GETHEADER_LIST );
881  // Never send 304s in debug mode
882  if ( $clientKeys !== false && !$context->getDebug() && in_array( $etag, $clientKeys ) ) {
883  // There's another bug in ob_gzhandler (see also the comment at
884  // the top of this function) that causes it to gzip even empty
885  // responses, meaning it's impossible to produce a truly empty
886  // response (because the gzip header is always there). This is
887  // a problem because 304 responses have to be completely empty
888  // per the HTTP spec, and Firefox behaves buggily when they're not.
889  // See also https://bugs.php.net/bug.php?id=51579
890  // To work around this, we tear down all output buffering before
891  // sending the 304.
892  wfResetOutputBuffers( /* $resetGzipEncoding = */ true );
893 
894  HttpStatus::header( 304 );
895 
896  $this->sendResponseHeaders( $context, $etag, false );
897  return true;
898  }
899  return false;
900  }
901 
910  protected function tryRespondFromFileCache(
911  ResourceFileCache $fileCache,
913  $etag
914  ) {
915  $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
916  // Buffer output to catch warnings.
917  ob_start();
918  // Get the maximum age the cache can be
919  $maxage = is_null( $context->getVersion() )
920  ? $rlMaxage['unversioned']['server']
921  : $rlMaxage['versioned']['server'];
922  // Minimum timestamp the cache file must have
923  $good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
924  if ( !$good ) {
925  try { // RL always hits the DB on file cache miss...
926  wfGetDB( DB_REPLICA );
927  } catch ( DBConnectionError $e ) { // ...check if we need to fallback to cache
928  $good = $fileCache->isCacheGood(); // cache existence check
929  }
930  }
931  if ( $good ) {
932  $ts = $fileCache->cacheTimestamp();
933  // Send content type and cache headers
934  $this->sendResponseHeaders( $context, $etag, false );
935  $response = $fileCache->fetchText();
936  // Capture any PHP warnings from the output buffer and append them to the
937  // response in a comment if we're in debug mode.
938  if ( $context->getDebug() ) {
939  $warnings = ob_get_contents();
940  if ( strlen( $warnings ) ) {
941  $response = self::makeComment( $warnings ) . $response;
942  }
943  }
944  // Remove the output buffer and output the response
945  ob_end_clean();
946  echo $response . "\n/* Cached {$ts} */";
947  return true; // cache hit
948  }
949  // Clear buffer
950  ob_end_clean();
951 
952  return false; // cache miss
953  }
954 
963  public static function makeComment( $text ) {
964  $encText = str_replace( '*/', '* /', $text );
965  return "/*\n$encText\n*/\n";
966  }
967 
974  public static function formatException( $e ) {
975  return self::makeComment( self::formatExceptionNoComment( $e ) );
976  }
977 
985  protected static function formatExceptionNoComment( $e ) {
986  global $wgShowExceptionDetails;
987 
988  if ( !$wgShowExceptionDetails ) {
990  }
991 
993  "\nBacktrace:\n" .
995  }
996 
1005  public function makeModuleResponse( ResourceLoaderContext $context,
1006  array $modules, array $missing = []
1007  ) {
1008  $out = '';
1009  $states = [];
1010 
1011  if ( !count( $modules ) && !count( $missing ) ) {
1012  return <<<MESSAGE
1013 /* This file is the Web entry point for MediaWiki's ResourceLoader:
1014  <https://www.mediawiki.org/wiki/ResourceLoader>. In this request,
1015  no modules were requested. Max made me put this here. */
1016 MESSAGE;
1017  }
1018 
1019  $image = $context->getImageObj();
1020  if ( $image ) {
1021  $data = $image->getImageData( $context );
1022  if ( $data === false ) {
1023  $data = '';
1024  $this->errors[] = 'Image generation failed';
1025  }
1026  return $data;
1027  }
1028 
1029  foreach ( $missing as $name ) {
1030  $states[$name] = 'missing';
1031  }
1032 
1033  // Generate output
1034  $isRaw = false;
1035 
1036  $filter = $context->getOnly() === 'styles' ? 'minify-css' : 'minify-js';
1037 
1038  foreach ( $modules as $name => $module ) {
1039  try {
1040  $content = $module->getModuleContent( $context );
1041  $implementKey = $name . '@' . $module->getVersionHash( $context );
1042  $strContent = '';
1043 
1044  // Append output
1045  switch ( $context->getOnly() ) {
1046  case 'scripts':
1047  $scripts = $content['scripts'];
1048  if ( is_string( $scripts ) ) {
1049  // Load scripts raw...
1050  $strContent = $scripts;
1051  } elseif ( is_array( $scripts ) ) {
1052  // ...except when $scripts is an array of URLs
1053  $strContent = self::makeLoaderImplementScript( $implementKey, $scripts, [], [], [] );
1054  }
1055  break;
1056  case 'styles':
1057  $styles = $content['styles'];
1058  // We no longer seperate into media, they are all combined now with
1059  // custom media type groups into @media .. {} sections as part of the css string.
1060  // Module returns either an empty array or a numerical array with css strings.
1061  $strContent = isset( $styles['css'] ) ? implode( '', $styles['css'] ) : '';
1062  break;
1063  default:
1064  $scripts = isset( $content['scripts'] ) ? $content['scripts'] : '';
1065  if ( is_string( $scripts ) ) {
1066  if ( $name === 'site' || $name === 'user' ) {
1067  // Legacy scripts that run in the global scope without a closure.
1068  // mw.loader.implement will use globalEval if scripts is a string.
1069  // Minify manually here, because general response minification is
1070  // not effective due it being a string literal, not a function.
1071  if ( !ResourceLoader::inDebugMode() ) {
1072  $scripts = self::filter( 'minify-js', $scripts ); // T107377
1073  }
1074  } else {
1075  $scripts = new XmlJsCode( $scripts );
1076  }
1077  }
1078  $strContent = self::makeLoaderImplementScript(
1079  $implementKey,
1080  $scripts,
1081  isset( $content['styles'] ) ? $content['styles'] : [],
1082  isset( $content['messagesBlob'] ) ? new XmlJsCode( $content['messagesBlob'] ) : [],
1083  isset( $content['templates'] ) ? $content['templates'] : []
1084  );
1085  break;
1086  }
1087 
1088  if ( !$context->getDebug() ) {
1089  $strContent = self::filter( $filter, $strContent );
1090  }
1091 
1092  $out .= $strContent;
1093 
1094  } catch ( Exception $e ) {
1095  $this->outputErrorAndLog( $e, 'Generating module package failed: {exception}' );
1096 
1097  // Respond to client with error-state instead of module implementation
1098  $states[$name] = 'error';
1099  unset( $modules[$name] );
1100  }
1101  $isRaw |= $module->isRaw();
1102  }
1103 
1104  // Update module states
1105  if ( $context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw ) {
1106  if ( count( $modules ) && $context->getOnly() === 'scripts' ) {
1107  // Set the state of modules loaded as only scripts to ready as
1108  // they don't have an mw.loader.implement wrapper that sets the state
1109  foreach ( $modules as $name => $module ) {
1110  $states[$name] = 'ready';
1111  }
1112  }
1113 
1114  // Set the state of modules we didn't respond to with mw.loader.implement
1115  if ( count( $states ) ) {
1116  $stateScript = self::makeLoaderStateScript( $states );
1117  if ( !$context->getDebug() ) {
1118  $stateScript = self::filter( 'minify-js', $stateScript );
1119  }
1120  $out .= $stateScript;
1121  }
1122  } else {
1123  if ( count( $states ) ) {
1124  $this->errors[] = 'Problematic modules: ' .
1125  FormatJson::encode( $states, ResourceLoader::inDebugMode() );
1126  }
1127  }
1128 
1129  return $out;
1130  }
1131 
1138  public function getModulesByMessage( $messageKey ) {
1139  $moduleNames = [];
1140  foreach ( $this->getModuleNames() as $moduleName ) {
1141  $module = $this->getModule( $moduleName );
1142  if ( in_array( $messageKey, $module->getMessages() ) ) {
1143  $moduleNames[] = $moduleName;
1144  }
1145  }
1146  return $moduleNames;
1147  }
1148 
1149  /* Static Methods */
1150 
1167  protected static function makeLoaderImplementScript(
1168  $name, $scripts, $styles, $messages, $templates
1169  ) {
1170  if ( $scripts instanceof XmlJsCode ) {
1171  $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
1172  } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) {
1173  throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
1174  }
1175  // mw.loader.implement requires 'styles', 'messages' and 'templates' to be objects (not
1176  // arrays). json_encode considers empty arrays to be numerical and outputs "[]" instead
1177  // of "{}". Force them to objects.
1178  $module = [
1179  $name,
1180  $scripts,
1181  (object)$styles,
1182  (object)$messages,
1183  (object)$templates,
1184  ];
1185  self::trimArray( $module );
1186 
1187  return Xml::encodeJsCall( 'mw.loader.implement', $module, ResourceLoader::inDebugMode() );
1188  }
1189 
1197  public static function makeMessageSetScript( $messages ) {
1198  return Xml::encodeJsCall(
1199  'mw.messages.set',
1200  [ (object)$messages ],
1201  ResourceLoader::inDebugMode()
1202  );
1203  }
1204 
1212  public static function makeCombinedStyles( array $stylePairs ) {
1213  $out = [];
1214  foreach ( $stylePairs as $media => $styles ) {
1215  // ResourceLoaderFileModule::getStyle can return the styles
1216  // as a string or an array of strings. This is to allow separation in
1217  // the front-end.
1218  $styles = (array)$styles;
1219  foreach ( $styles as $style ) {
1220  $style = trim( $style );
1221  // Don't output an empty "@media print { }" block (T42498)
1222  if ( $style !== '' ) {
1223  // Transform the media type based on request params and config
1224  // The way that this relies on $wgRequest to propagate request params is slightly evil
1225  $media = OutputPage::transformCssMedia( $media );
1226 
1227  if ( $media === '' || $media == 'all' ) {
1228  $out[] = $style;
1229  } elseif ( is_string( $media ) ) {
1230  $out[] = "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "}";
1231  }
1232  // else: skip
1233  }
1234  }
1235  }
1236  return $out;
1237  }
1238 
1253  public static function makeLoaderStateScript( $name, $state = null ) {
1254  if ( is_array( $name ) ) {
1255  return Xml::encodeJsCall(
1256  'mw.loader.state',
1257  [ $name ],
1258  ResourceLoader::inDebugMode()
1259  );
1260  } else {
1261  return Xml::encodeJsCall(
1262  'mw.loader.state',
1263  [ $name, $state ],
1264  ResourceLoader::inDebugMode()
1265  );
1266  }
1267  }
1268 
1283  public static function makeCustomLoaderScript( $name, $version, $dependencies,
1284  $group, $source, $script
1285  ) {
1286  $script = str_replace( "\n", "\n\t", trim( $script ) );
1287  return Xml::encodeJsCall(
1288  "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )",
1289  [ $name, $version, $dependencies, $group, $source ],
1290  ResourceLoader::inDebugMode()
1291  );
1292  }
1293 
1294  private static function isEmptyObject( stdClass $obj ) {
1295  foreach ( $obj as $key => $value ) {
1296  return false;
1297  }
1298  return true;
1299  }
1300 
1313  private static function trimArray( array &$array ) {
1314  $i = count( $array );
1315  while ( $i-- ) {
1316  if ( $array[$i] === null
1317  || $array[$i] === []
1318  || ( $array[$i] instanceof XmlJsCode && $array[$i]->value === '{}' )
1319  || ( $array[$i] instanceof stdClass && self::isEmptyObject( $array[$i] ) )
1320  ) {
1321  unset( $array[$i] );
1322  } else {
1323  break;
1324  }
1325  }
1326  }
1327 
1355  public static function makeLoaderRegisterScript( $name, $version = null,
1356  $dependencies = null, $group = null, $source = null, $skip = null
1357  ) {
1358  if ( is_array( $name ) ) {
1359  // Build module name index
1360  $index = [];
1361  foreach ( $name as $i => &$module ) {
1362  $index[$module[0]] = $i;
1363  }
1364 
1365  // Transform dependency names into indexes when possible, they will be resolved by
1366  // mw.loader.register on the other end
1367  foreach ( $name as &$module ) {
1368  if ( isset( $module[2] ) ) {
1369  foreach ( $module[2] as &$dependency ) {
1370  if ( isset( $index[$dependency] ) ) {
1371  $dependency = $index[$dependency];
1372  }
1373  }
1374  }
1375  }
1376 
1377  array_walk( $name, [ 'self', 'trimArray' ] );
1378 
1379  return Xml::encodeJsCall(
1380  'mw.loader.register',
1381  [ $name ],
1382  ResourceLoader::inDebugMode()
1383  );
1384  } else {
1385  $registration = [ $name, $version, $dependencies, $group, $source, $skip ];
1386  self::trimArray( $registration );
1387  return Xml::encodeJsCall(
1388  'mw.loader.register',
1389  $registration,
1390  ResourceLoader::inDebugMode()
1391  );
1392  }
1393  }
1394 
1409  public static function makeLoaderSourcesScript( $id, $loadUrl = null ) {
1410  if ( is_array( $id ) ) {
1411  return Xml::encodeJsCall(
1412  'mw.loader.addSource',
1413  [ $id ],
1414  ResourceLoader::inDebugMode()
1415  );
1416  } else {
1417  return Xml::encodeJsCall(
1418  'mw.loader.addSource',
1419  [ $id, $loadUrl ],
1420  ResourceLoader::inDebugMode()
1421  );
1422  }
1423  }
1424 
1433  public static function makeLoaderConditionalScript( $script ) {
1434  return '(window.RLQ=window.RLQ||[]).push(function(){' .
1435  trim( $script ) . '});';
1436  }
1437 
1447  public static function makeInlineScript( $script ) {
1448  $js = self::makeLoaderConditionalScript( $script );
1449  return new WrappedString(
1450  Html::inlineScript( $js ),
1451  '<script>(window.RLQ=window.RLQ||[]).push(function(){',
1452  '});</script>'
1453  );
1454  }
1455 
1463  public static function makeConfigSetScript( array $configuration ) {
1464  return Xml::encodeJsCall(
1465  'mw.config.set',
1466  [ $configuration ],
1467  ResourceLoader::inDebugMode()
1468  );
1469  }
1470 
1479  public static function makePackedModulesString( $modules ) {
1480  $groups = []; // [ prefix => [ suffixes ] ]
1481  foreach ( $modules as $module ) {
1482  $pos = strrpos( $module, '.' );
1483  $prefix = $pos === false ? '' : substr( $module, 0, $pos );
1484  $suffix = $pos === false ? $module : substr( $module, $pos + 1 );
1485  $groups[$prefix][] = $suffix;
1486  }
1487 
1488  $arr = [];
1489  foreach ( $groups as $prefix => $suffixes ) {
1490  $p = $prefix === '' ? '' : $prefix . '.';
1491  $arr[] = $p . implode( ',', $suffixes );
1492  }
1493  $str = implode( '|', $arr );
1494  return $str;
1495  }
1496 
1502  public static function inDebugMode() {
1503  if ( self::$debugMode === null ) {
1504  global $wgRequest, $wgResourceLoaderDebug;
1505  self::$debugMode = $wgRequest->getFuzzyBool( 'debug',
1506  $wgRequest->getCookie( 'resourceLoaderDebug', '', $wgResourceLoaderDebug )
1507  );
1508  }
1509  return self::$debugMode;
1510  }
1511 
1519  public static function clearCache() {
1520  self::$debugMode = null;
1521  }
1522 
1532  public function createLoaderURL( $source, ResourceLoaderContext $context,
1533  $extraQuery = []
1534  ) {
1535  $query = self::createLoaderQuery( $context, $extraQuery );
1536  $script = $this->getLoadScript( $source );
1537 
1538  return wfAppendQuery( $script, $query );
1539  }
1540 
1550  protected static function createLoaderQuery( ResourceLoaderContext $context, $extraQuery = [] ) {
1551  return self::makeLoaderQuery(
1552  $context->getModules(),
1553  $context->getLanguage(),
1554  $context->getSkin(),
1555  $context->getUser(),
1556  $context->getVersion(),
1557  $context->getDebug(),
1558  $context->getOnly(),
1559  $context->getRequest()->getBool( 'printable' ),
1560  $context->getRequest()->getBool( 'handheld' ),
1561  $extraQuery
1562  );
1563  }
1564 
1582  public static function makeLoaderQuery( $modules, $lang, $skin, $user = null,
1583  $version = null, $debug = false, $only = null, $printable = false,
1584  $handheld = false, $extraQuery = []
1585  ) {
1586  $query = [
1587  'modules' => self::makePackedModulesString( $modules ),
1588  'lang' => $lang,
1589  'skin' => $skin,
1590  'debug' => $debug ? 'true' : 'false',
1591  ];
1592  if ( $user !== null ) {
1593  $query['user'] = $user;
1594  }
1595  if ( $version !== null ) {
1596  $query['version'] = $version;
1597  }
1598  if ( $only !== null ) {
1599  $query['only'] = $only;
1600  }
1601  if ( $printable ) {
1602  $query['printable'] = 1;
1603  }
1604  if ( $handheld ) {
1605  $query['handheld'] = 1;
1606  }
1607  $query += $extraQuery;
1608 
1609  // Make queries uniform in order
1610  ksort( $query );
1611  return $query;
1612  }
1613 
1623  public static function isValidModuleName( $moduleName ) {
1624  return strcspn( $moduleName, '!,|', 0, 255 ) === strlen( $moduleName );
1625  }
1626 
1636  public function getLessCompiler( $extraVars = [] ) {
1637  // When called from the installer, it is possible that a required PHP extension
1638  // is missing (at least for now; see T49564). If this is the case, throw an
1639  // exception (caught by the installer) to prevent a fatal error later on.
1640  if ( !class_exists( 'Less_Parser' ) ) {
1641  throw new MWException( 'MediaWiki requires the less.php parser' );
1642  }
1643 
1644  $parser = new Less_Parser;
1645  $parser->ModifyVars( array_merge( $this->getLessVars(), $extraVars ) );
1646  $parser->SetImportDirs(
1647  array_fill_keys( $this->config->get( 'ResourceLoaderLESSImportPaths' ), '' )
1648  );
1649  $parser->SetOption( 'relativeUrls', false );
1650 
1651  return $parser;
1652  }
1653 
1660  public function getLessVars() {
1661  if ( !$this->lessVars ) {
1662  $lessVars = $this->config->get( 'ResourceLoaderLESSVars' );
1663  Hooks::run( 'ResourceLoaderGetLessVars', [ &$lessVars ] );
1664  $this->lessVars = $lessVars;
1665  }
1666  return $this->lessVars;
1667  }
1668 }
ResourceLoaderContext
Object passed around to modules which contains information about the state of a specific loader reque...
Definition: ResourceLoaderContext.php:32
object
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest object
Definition: globals.txt:25
$context
error also a ContextSource you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2612
wfResetOutputBuffers
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
Definition: GlobalFunctions.php:1802
errors
if the prop value should be in the metadata multi language array can modify can modify indexed by page_id indexed by prefixed DB keys can modify can modify can modify this should be populated with an alert message to that effect to be fed to an HTMLForm object and populate $result with the reason in the form of error messages should be plain text with no special etc to show that they re errors
Definition: hooks.txt:1721
CSSMin\minify
static minify( $css)
Removes whitespace from CSS data.
Definition: CSSMin.php:452
FileCacheBase\saveText
saveText( $text)
Save and compress text to the cache.
Definition: FileCacheBase.php:159
ResourceFileCache\newFromContext
static newFromContext(ResourceLoaderContext $context)
Construct an ResourceFileCache from a context.
Definition: ResourceFileCache.php:40
FileCacheBase\isCacheGood
isCacheGood( $timestamp='')
Check if up to date cache file exists.
Definition: FileCacheBase.php:117
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
captcha-old.count
count
Definition: captcha-old.py:225
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1954
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1994
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
ResourceLoaderFilePath
An object to represent a path to a JavaScript/CSS file, along with a remote and local base path,...
Definition: ResourceLoaderFilePath.php:28
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:246
JavaScriptMinifier\minify
static minify( $s, $statementsOnOwnLine=false, $maxLineLength=1000)
Returns minified JavaScript code.
Definition: JavaScriptMinifier.php:81
$res
$res
Definition: database.txt:21
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
ContextSource\getRequest
getRequest()
Get the WebRequest object.
Definition: ContextSource.php:78
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:133
$messages
$messages
Definition: LogTests.i18n.php:8
Html\inlineScript
static inlineScript( $contents)
Output a "<script>" tag with the given contents.
Definition: Html.php:583
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
OutputPage\transformCssMedia
static transformCssMedia( $media)
Transform "media" attribute based on request parameters.
Definition: OutputPage.php:3817
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:143
$debug
$debug
Definition: mcc.php:31
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:500
XmlJsCode
A wrapper class which causes Xml::encodeJsVar() and Xml::encodeJsCall() to interpret a given string a...
Definition: Xml.php:847
Xml\encodeJsCall
static encodeJsCall( $name, $args, $pretty=false)
Create a call to a JavaScript function.
Definition: Xml.php:645
$query
null for the wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1572
MWExceptionHandler\getPublicLogMessage
static getPublicLogMessage( $e)
Definition: MWExceptionHandler.php:450
FormatJson\decode
static decode( $value, $assoc=false)
Decodes a JSON string.
Definition: FormatJson.php:187
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:127
MWException
MediaWiki exception.
Definition: MWException.php:26
ResourceLoaderWikiModule\preloadTitleInfo
static preloadTitleInfo(ResourceLoaderContext $context, IDatabase $db, array $moduleNames)
Definition: ResourceLoaderWikiModule.php:362
$blob
$blob
Definition: testCompression.php:63
$content
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1049
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
MWExceptionHandler\getLogMessage
static getLogMessage( $e)
Get a message formatting the exception message and its origin.
Definition: MWExceptionHandler.php:435
$modules
$modules
Definition: HTMLFormElement.php:12
$IP
$IP
Definition: update.php:3
ContextSource\getSkin
getSkin()
Get the Skin object.
Definition: ContextSource.php:153
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:2536
ResourceLoaderFileModule\extractBasePaths
static extractBasePaths( $options=[], $localBasePath=null, $remoteBasePath=null)
Extract a pair of local and remote base paths from module definition information.
Definition: ResourceLoaderFileModule.php:310
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
ResourceLoaderModule\expandRelativePaths
static expandRelativePaths(array $filePaths)
Expand directories relative to $IP.
Definition: ResourceLoaderModule.php:546
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
FileCacheBase\fetchText
fetchText()
Get the uncompressed text from the cache.
Definition: FileCacheBase.php:144
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
$image
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check $image
Definition: hooks.txt:783
or
or
Definition: COPYING.txt:140
MessageBlobStore\get
get(ResourceLoader $resourceLoader, $modules, $lang)
Definition: MessageBlobStore.php:127
ResourceFileCache\useFileCache
static useFileCache(ResourceLoaderContext $context)
Check if an RL request can be cached.
Definition: ResourceFileCache.php:66
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2122
value
$status value
Definition: SyntaxHighlight_GeSHi.class.php:311
$value
$value
Definition: styleTest.css.php:45
CACHE_ANYTHING
const CACHE_ANYTHING
Definition: Defines.php:99
$response
this hook is for auditing only $response
Definition: hooks.txt:783
ResourceLoaderModule
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
Definition: ResourceLoaderModule.php:34
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
HttpStatus\header
static header( $code)
Output an HTTP status code header.
Definition: HttpStatus.php:96
WebRequest\GETHEADER_LIST
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:45
$path
$path
Definition: NoLocalSettings.php:26
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
$skin
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned $skin
Definition: hooks.txt:1956
Wikimedia\Rdbms\DBConnectionError
Definition: DBConnectionError.php:26
from
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable from
Definition: APACHE-LICENSE-2.0.txt:43
MessageBlobStore
This class generates message blobs for use by ResourceLoader modules.
Definition: MessageBlobStore.php:37
$source
$source
Definition: mwdoc-filter.php:45
true
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1956
$hashes
$hashes
Definition: testCompression.php:64
FileCacheBase\incrMissesRecent
incrMissesRecent(WebRequest $request)
Roughly increments the cache misses in the last hour by unique visitors.
Definition: FileCacheBase.php:231
definition
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this definition
Definition: APACHE-LICENSE-2.0.txt:49
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:639
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
ResourceFileCache\isCacheWorthy
isCacheWorthy()
Item has many recent cache misses.
Definition: ResourceFileCache.php:107
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
ResourceFileCache
ResourceLoader request result caching in the file system.
Definition: ResourceFileCache.php:29
MWExceptionHandler\getRedactedTraceAsString
static getRedactedTraceAsString( $e)
Generate a string representation of an exception's stack trace.
Definition: MWExceptionHandler.php:313
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
array
the array() calling protocol came about after MediaWiki 1.4rc1.
FileCacheBase\cacheTimestamp
cacheTimestamp()
Get the last-modified timestamp of the cache file.
Definition: FileCacheBase.php:103
MediaWiki\HeaderCallback\warnIfHeadersSent
static warnIfHeadersSent()
Log a warning message if headers have already been sent.
Definition: HeaderCallback.php:57
MWExceptionHandler\logException
static logException( $e, $catcher=self::CAUGHT_BY_OTHER)
Log an exception to the exception log (if enabled).
Definition: MWExceptionHandler.php:596
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:288
$out
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:783