MediaWiki REL1_34
rebuildLocalisationCache.php
Go to the documentation of this file.
1<?php
2
35
36require_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 $services = MediaWikiServices::getInstance();
92 LocalisationCache::CONSTRUCTOR_OPTIONS,
93 $conf,
94 $services->getMainConfig()
95 ),
96 LocalisationCache::getStoreFromConf( $conf, $wgCacheDirectory ),
97 LoggerFactory::getInstance( 'localisation' ),
98 [ function () use ( $services ) {
99 MessageBlobStore::clearGlobalCacheEntry( $services->getMainWANObjectCache() );
100 } ],
101 $services->getLanguageNameUtils()
102 );
103
104 $allCodes = array_keys( $services
105 ->getLanguageNameUtils()
106 ->getLanguageNames( null, 'mwfile' ) );
107 if ( $this->hasOption( 'lang' ) ) {
108 # Validate requested languages
109 $codes = array_intersect( $allCodes,
110 explode( ',', $this->getOption( 'lang' ) ) );
111 # Bailed out if nothing is left
112 if ( count( $codes ) == 0 ) {
113 $this->fatalError( 'None of the languages specified exists.' );
114 }
115 } else {
116 # By default get all languages
117 $codes = $allCodes;
118 }
119 sort( $codes );
120
121 // Initialise and split into chunks
122 $numRebuilt = 0;
123 $total = count( $codes );
124 $chunks = array_chunk( $codes, ceil( count( $codes ) / $threads ) );
125 $pids = [];
126 $parentStatus = 0;
127 foreach ( $chunks as $codes ) {
128 // Do not fork for only one thread
129 $pid = ( $threads > 1 ) ? pcntl_fork() : -1;
130
131 if ( $pid === 0 ) {
132 // Child, reseed because there is no bug in PHP:
133 // https://bugs.php.net/bug.php?id=42465
134 mt_srand( getmypid() );
135
136 $this->doRebuild( $codes, $lc, $force );
137 exit( 0 );
138 } elseif ( $pid === -1 ) {
139 // Fork failed or one thread, do it serialized
140 $numRebuilt += $this->doRebuild( $codes, $lc, $force );
141 } else {
142 // Main thread
143 $pids[] = $pid;
144 }
145 }
146 // Wait for all children
147 foreach ( $pids as $pid ) {
148 $status = 0;
149 pcntl_waitpid( $pid, $status );
150 if ( pcntl_wexitstatus( $status ) ) {
151 // Pass a fatal error code through to the caller
152 $parentStatus = pcntl_wexitstatus( $status );
153 }
154 }
155
156 if ( !$pids ) {
157 $this->output( "$numRebuilt languages rebuilt out of $total\n" );
158 if ( $numRebuilt === 0 ) {
159 $this->output( "Use --force to rebuild the caches which are still fresh.\n" );
160 }
161 }
162 if ( $parentStatus ) {
163 exit( $parentStatus );
164 }
165 }
166
175 private function doRebuild( $codes, $lc, $force ) {
176 $numRebuilt = 0;
177 foreach ( $codes as $code ) {
178 if ( $force || $lc->isExpired( $code ) ) {
179 $this->output( "Rebuilding $code...\n" );
180 $lc->recache( $code );
181 $numRebuilt++;
182 }
183 }
184
185 return $numRebuilt;
186 }
187
193 public function setForce( $forced = true ) {
194 $this->mOptions['force'] = $forced;
195 }
196}
197
198$maintClass = RebuildLocalisationCache::class;
199require_once RUN_MAINTENANCE_IF_MAIN;
$wgLanguageCode
Site language code.
$wgCacheDirectory
Directory for caching data in the local filesystem.
$wgLocalisationCacheConf
Localisation cache configuration.
wfIsWindows()
Check if the operating system is Windows.
const RUN_MAINTENANCE_IF_MAIN
A localisation cache optimised for loading large amounts of data for many languages.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option exists.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
A class for passing options to services.
PSR-3 logger instance factory.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static clearGlobalCacheEntry(WANObjectCache $cache)
Invalidate cache keys for all known modules.
Maintenance script to rebuild the localisation cache.
finalSetup()
Handle some last-minute setup here.
setForce( $forced=true)
Sets whether a run of this maintenance script has the force parameter set.
doRebuild( $codes, $lc, $force)
Helper function to rebuild list of languages codes.