MediaWiki  master
rebuildLocalisationCache.php
Go to the documentation of this file.
1 <?php
2 
35 
36 require_once __DIR__ . '/Maintenance.php';
37 
44  public function __construct() {
45  parent::__construct();
46  $this->addDescription( 'Rebuild the localisation cache' );
47  $this->addOption( 'force', 'Rebuild all files, even ones not out of date' );
48  $this->addOption( 'threads', 'Fork more than one thread', false, true );
49  $this->addOption( 'outdir', 'Override the output directory (normally $wgCacheDirectory)',
50  false, true );
51  $this->addOption( 'lang', 'Only rebuild these languages, comma separated.',
52  false, true );
53  }
54 
55  public function finalSetup() {
56  # This script needs to be run to build the inital l10n cache. But if
57  # $wgLanguageCode is not 'en', it won't be able to run because there is
58  # no l10n cache. Break the cycle by forcing $wgLanguageCode = 'en'.
59  global $wgLanguageCode;
60  $wgLanguageCode = 'en';
61  parent::finalSetup();
62  }
63 
64  public function execute() {
66 
67  $force = $this->hasOption( 'force' );
68  $threads = $this->getOption( 'threads', 1 );
69  if ( $threads < 1 || $threads != intval( $threads ) ) {
70  $this->output( "Invalid thread count specified; running single-threaded.\n" );
71  $threads = 1;
72  }
73  if ( $threads > 1 && wfIsWindows() ) {
74  $this->output( "Threaded rebuild is not supported on Windows; running single-threaded.\n" );
75  $threads = 1;
76  }
77  if ( $threads > 1 && !function_exists( 'pcntl_fork' ) ) {
78  $this->output( "PHP pcntl extension is not present; running single-threaded.\n" );
79  $threads = 1;
80  }
81 
83  $conf['manualRecache'] = false; // Allow fallbacks to create CDB files
84  $conf['forceRecache'] = $force || !empty( $conf['forceRecache'] );
85  if ( $this->hasOption( 'outdir' ) ) {
86  $conf['storeDirectory'] = $this->getOption( 'outdir' );
87  }
88  // XXX Copy-pasted from ServiceWiring.php. Do we need a factory for this one caller?
89  $lc = new LocalisationCacheBulkLoad(
90  new ServiceOptions(
91  LocalisationCache::CONSTRUCTOR_OPTIONS,
92  $conf,
93  MediaWikiServices::getInstance()->getMainConfig()
94  ),
95  LocalisationCache::getStoreFromConf( $conf, $wgCacheDirectory ),
96  LoggerFactory::getInstance( 'localisation' ),
97  [ function () {
98  MediaWikiServices::getInstance()->getResourceLoader()
99  ->getMessageBlobStore()->clear();
100  } ],
101  MediaWikiServices::getInstance()->getLanguageNameUtils()
102  );
103 
104  $allCodes = array_keys( Language::fetchLanguageNames( null, 'mwfile' ) );
105  if ( $this->hasOption( 'lang' ) ) {
106  # Validate requested languages
107  $codes = array_intersect( $allCodes,
108  explode( ',', $this->getOption( 'lang' ) ) );
109  # Bailed out if nothing is left
110  if ( count( $codes ) == 0 ) {
111  $this->fatalError( 'None of the languages specified exists.' );
112  }
113  } else {
114  # By default get all languages
115  $codes = $allCodes;
116  }
117  sort( $codes );
118 
119  // Initialise and split into chunks
120  $numRebuilt = 0;
121  $total = count( $codes );
122  $chunks = array_chunk( $codes, ceil( count( $codes ) / $threads ) );
123  $pids = [];
124  $parentStatus = 0;
125  foreach ( $chunks as $codes ) {
126  // Do not fork for only one thread
127  $pid = ( $threads > 1 ) ? pcntl_fork() : -1;
128 
129  if ( $pid === 0 ) {
130  // Child, reseed because there is no bug in PHP:
131  // https://bugs.php.net/bug.php?id=42465
132  mt_srand( getmypid() );
133 
134  $this->doRebuild( $codes, $lc, $force );
135  exit( 0 );
136  } elseif ( $pid === -1 ) {
137  // Fork failed or one thread, do it serialized
138  $numRebuilt += $this->doRebuild( $codes, $lc, $force );
139  } else {
140  // Main thread
141  $pids[] = $pid;
142  }
143  }
144  // Wait for all children
145  foreach ( $pids as $pid ) {
146  $status = 0;
147  pcntl_waitpid( $pid, $status );
148  if ( pcntl_wexitstatus( $status ) ) {
149  // Pass a fatal error code through to the caller
150  $parentStatus = pcntl_wexitstatus( $status );
151  }
152  }
153 
154  if ( !$pids ) {
155  $this->output( "$numRebuilt languages rebuilt out of $total\n" );
156  if ( $numRebuilt === 0 ) {
157  $this->output( "Use --force to rebuild the caches which are still fresh.\n" );
158  }
159  }
160  if ( $parentStatus ) {
161  exit( $parentStatus );
162  }
163  }
164 
173  private function doRebuild( $codes, $lc, $force ) {
174  $numRebuilt = 0;
175  foreach ( $codes as $code ) {
176  if ( $force || $lc->isExpired( $code ) ) {
177  $this->output( "Rebuilding $code...\n" );
178  $lc->recache( $code );
179  $numRebuilt++;
180  }
181  }
182 
183  return $numRebuilt;
184  }
185 
191  public function setForce( $forced = true ) {
192  $this->mOptions['force'] = $forced;
193  }
194 }
195 
196 $maintClass = RebuildLocalisationCache::class;
197 require_once RUN_MAINTENANCE_IF_MAIN;
A localisation cache optimised for loading large amounts of data for many languages.
static fetchLanguageNames( $inLanguage=self::AS_AUTONYMS, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:790
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:39
Maintenance script to rebuild the localisation cache.
getOption( $name, $default=null)
Get an option, or return the default.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:86
hasOption( $name)
Checks to see if a particular option exists.
wfIsWindows()
Check if the operating system is Windows.
$wgCacheDirectory
Directory for caching data in the local filesystem.
A class for passing options to services.
$wgLanguageCode
Site language code.
$wgLocalisationCacheConf
Localisation cache configuration.
addDescription( $text)
Set the description text.
output( $out, $channel=null)
Throw some output to the user.
setForce( $forced=true)
Sets whether a run of this maintenance script has the force parameter set.
static getStoreFromConf(array $conf, $fallbackCacheDir)
Return a suitable LCStore as specified by the given configuration.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
doRebuild( $codes, $lc, $force)
Helper function to rebuild list of languages codes.