This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Base code for MediaWiki installer. |
||
4 | * |
||
5 | * DO NOT PATCH THIS FILE IF YOU NEED TO CHANGE INSTALLER BEHAVIOR IN YOUR PACKAGE! |
||
6 | * See mw-config/overrides/README for details. |
||
7 | * |
||
8 | * This program is free software; you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License as published by |
||
10 | * the Free Software Foundation; either version 2 of the License, or |
||
11 | * (at your option) any later version. |
||
12 | * |
||
13 | * This program is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License along |
||
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
21 | * http://www.gnu.org/copyleft/gpl.html |
||
22 | * |
||
23 | * @file |
||
24 | * @ingroup Deployment |
||
25 | */ |
||
26 | use MediaWiki\MediaWikiServices; |
||
27 | |||
28 | /** |
||
29 | * This documentation group collects source code files with deployment functionality. |
||
30 | * |
||
31 | * @defgroup Deployment Deployment |
||
32 | */ |
||
33 | |||
34 | /** |
||
35 | * Base installer class. |
||
36 | * |
||
37 | * This class provides the base for installation and update functionality |
||
38 | * for both MediaWiki core and extensions. |
||
39 | * |
||
40 | * @ingroup Deployment |
||
41 | * @since 1.17 |
||
42 | */ |
||
43 | abstract class Installer { |
||
44 | |||
45 | /** |
||
46 | * The oldest version of PCRE we can support. |
||
47 | * |
||
48 | * Defining this is necessary because PHP may be linked with a system version |
||
49 | * of PCRE, which may be older than that bundled with the minimum PHP version. |
||
50 | */ |
||
51 | const MINIMUM_PCRE_VERSION = '7.2'; |
||
52 | |||
53 | /** |
||
54 | * @var array |
||
55 | */ |
||
56 | protected $settings; |
||
57 | |||
58 | /** |
||
59 | * List of detected DBs, access using getCompiledDBs(). |
||
60 | * |
||
61 | * @var array |
||
62 | */ |
||
63 | protected $compiledDBs; |
||
64 | |||
65 | /** |
||
66 | * Cached DB installer instances, access using getDBInstaller(). |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | protected $dbInstallers = []; |
||
71 | |||
72 | /** |
||
73 | * Minimum memory size in MB. |
||
74 | * |
||
75 | * @var int |
||
76 | */ |
||
77 | protected $minMemorySize = 50; |
||
78 | |||
79 | /** |
||
80 | * Cached Title, used by parse(). |
||
81 | * |
||
82 | * @var Title |
||
83 | */ |
||
84 | protected $parserTitle; |
||
85 | |||
86 | /** |
||
87 | * Cached ParserOptions, used by parse(). |
||
88 | * |
||
89 | * @var ParserOptions |
||
90 | */ |
||
91 | protected $parserOptions; |
||
92 | |||
93 | /** |
||
94 | * Known database types. These correspond to the class names <type>Installer, |
||
95 | * and are also MediaWiki database types valid for $wgDBtype. |
||
96 | * |
||
97 | * To add a new type, create a <type>Installer class and a Database<type> |
||
98 | * class, and add a config-type-<type> message to MessagesEn.php. |
||
99 | * |
||
100 | * @var array |
||
101 | */ |
||
102 | protected static $dbTypes = [ |
||
103 | 'mysql', |
||
104 | 'postgres', |
||
105 | 'oracle', |
||
106 | 'mssql', |
||
107 | 'sqlite', |
||
108 | ]; |
||
109 | |||
110 | /** |
||
111 | * A list of environment check methods called by doEnvironmentChecks(). |
||
112 | * These may output warnings using showMessage(), and/or abort the |
||
113 | * installation process by returning false. |
||
114 | * |
||
115 | * For the WebInstaller these are only called on the Welcome page, |
||
116 | * if these methods have side-effects that should affect later page loads |
||
117 | * (as well as the generated stylesheet), use envPreps instead. |
||
118 | * |
||
119 | * @var array |
||
120 | */ |
||
121 | protected $envChecks = [ |
||
122 | 'envCheckDB', |
||
123 | 'envCheckBrokenXML', |
||
124 | 'envCheckPCRE', |
||
125 | 'envCheckMemory', |
||
126 | 'envCheckCache', |
||
127 | 'envCheckModSecurity', |
||
128 | 'envCheckDiff3', |
||
129 | 'envCheckGraphics', |
||
130 | 'envCheckGit', |
||
131 | 'envCheckServer', |
||
132 | 'envCheckPath', |
||
133 | 'envCheckShellLocale', |
||
134 | 'envCheckUploadsDirectory', |
||
135 | 'envCheckLibicu', |
||
136 | 'envCheckSuhosinMaxValueLength', |
||
137 | ]; |
||
138 | |||
139 | /** |
||
140 | * A list of environment preparation methods called by doEnvironmentPreps(). |
||
141 | * |
||
142 | * @var array |
||
143 | */ |
||
144 | protected $envPreps = [ |
||
145 | 'envPrepServer', |
||
146 | 'envPrepPath', |
||
147 | ]; |
||
148 | |||
149 | /** |
||
150 | * MediaWiki configuration globals that will eventually be passed through |
||
151 | * to LocalSettings.php. The names only are given here, the defaults |
||
152 | * typically come from DefaultSettings.php. |
||
153 | * |
||
154 | * @var array |
||
155 | */ |
||
156 | protected $defaultVarNames = [ |
||
157 | 'wgSitename', |
||
158 | 'wgPasswordSender', |
||
159 | 'wgLanguageCode', |
||
160 | 'wgRightsIcon', |
||
161 | 'wgRightsText', |
||
162 | 'wgRightsUrl', |
||
163 | 'wgEnableEmail', |
||
164 | 'wgEnableUserEmail', |
||
165 | 'wgEnotifUserTalk', |
||
166 | 'wgEnotifWatchlist', |
||
167 | 'wgEmailAuthentication', |
||
168 | 'wgDBname', |
||
169 | 'wgDBtype', |
||
170 | 'wgDiff3', |
||
171 | 'wgImageMagickConvertCommand', |
||
172 | 'wgGitBin', |
||
173 | 'IP', |
||
174 | 'wgScriptPath', |
||
175 | 'wgMetaNamespace', |
||
176 | 'wgDeletedDirectory', |
||
177 | 'wgEnableUploads', |
||
178 | 'wgShellLocale', |
||
179 | 'wgSecretKey', |
||
180 | 'wgUseInstantCommons', |
||
181 | 'wgUpgradeKey', |
||
182 | 'wgDefaultSkin', |
||
183 | 'wgPingback', |
||
184 | ]; |
||
185 | |||
186 | /** |
||
187 | * Variables that are stored alongside globals, and are used for any |
||
188 | * configuration of the installation process aside from the MediaWiki |
||
189 | * configuration. Map of names to defaults. |
||
190 | * |
||
191 | * @var array |
||
192 | */ |
||
193 | protected $internalDefaults = [ |
||
194 | '_UserLang' => 'en', |
||
195 | '_Environment' => false, |
||
196 | '_RaiseMemory' => false, |
||
197 | '_UpgradeDone' => false, |
||
198 | '_InstallDone' => false, |
||
199 | '_Caches' => [], |
||
200 | '_InstallPassword' => '', |
||
201 | '_SameAccount' => true, |
||
202 | '_CreateDBAccount' => false, |
||
203 | '_NamespaceType' => 'site-name', |
||
204 | '_AdminName' => '', // will be set later, when the user selects language |
||
205 | '_AdminPassword' => '', |
||
206 | '_AdminPasswordConfirm' => '', |
||
207 | '_AdminEmail' => '', |
||
208 | '_Subscribe' => false, |
||
209 | '_SkipOptional' => 'continue', |
||
210 | '_RightsProfile' => 'wiki', |
||
211 | '_LicenseCode' => 'none', |
||
212 | '_CCDone' => false, |
||
213 | '_Extensions' => [], |
||
214 | '_Skins' => [], |
||
215 | '_MemCachedServers' => '', |
||
216 | '_UpgradeKeySupplied' => false, |
||
217 | '_ExistingDBSettings' => false, |
||
218 | |||
219 | // $wgLogo is probably wrong (bug 48084); set something that will work. |
||
220 | // Single quotes work fine here, as LocalSettingsGenerator outputs this unescaped. |
||
221 | 'wgLogo' => '$wgResourceBasePath/resources/assets/wiki.png', |
||
222 | 'wgAuthenticationTokenVersion' => 1, |
||
223 | ]; |
||
224 | |||
225 | /** |
||
226 | * The actual list of installation steps. This will be initialized by getInstallSteps() |
||
227 | * |
||
228 | * @var array |
||
229 | */ |
||
230 | private $installSteps = []; |
||
231 | |||
232 | /** |
||
233 | * Extra steps for installation, for things like DatabaseInstallers to modify |
||
234 | * |
||
235 | * @var array |
||
236 | */ |
||
237 | protected $extraInstallSteps = []; |
||
238 | |||
239 | /** |
||
240 | * Known object cache types and the functions used to test for their existence. |
||
241 | * |
||
242 | * @var array |
||
243 | */ |
||
244 | protected $objectCaches = [ |
||
245 | 'xcache' => 'xcache_get', |
||
246 | 'apc' => 'apc_fetch', |
||
247 | 'apcu' => 'apcu_fetch', |
||
248 | 'wincache' => 'wincache_ucache_get' |
||
249 | ]; |
||
250 | |||
251 | /** |
||
252 | * User rights profiles. |
||
253 | * |
||
254 | * @var array |
||
255 | */ |
||
256 | public $rightsProfiles = [ |
||
257 | 'wiki' => [], |
||
258 | 'no-anon' => [ |
||
259 | '*' => [ 'edit' => false ] |
||
260 | ], |
||
261 | 'fishbowl' => [ |
||
262 | '*' => [ |
||
263 | 'createaccount' => false, |
||
264 | 'edit' => false, |
||
265 | ], |
||
266 | ], |
||
267 | 'private' => [ |
||
268 | '*' => [ |
||
269 | 'createaccount' => false, |
||
270 | 'edit' => false, |
||
271 | 'read' => false, |
||
272 | ], |
||
273 | ], |
||
274 | ]; |
||
275 | |||
276 | /** |
||
277 | * License types. |
||
278 | * |
||
279 | * @var array |
||
280 | */ |
||
281 | public $licenses = [ |
||
282 | 'cc-by' => [ |
||
283 | 'url' => 'https://creativecommons.org/licenses/by/4.0/', |
||
284 | 'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by.png', |
||
285 | ], |
||
286 | 'cc-by-sa' => [ |
||
287 | 'url' => 'https://creativecommons.org/licenses/by-sa/4.0/', |
||
288 | 'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-sa.png', |
||
289 | ], |
||
290 | 'cc-by-nc-sa' => [ |
||
291 | 'url' => 'https://creativecommons.org/licenses/by-nc-sa/4.0/', |
||
292 | 'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-by-nc-sa.png', |
||
293 | ], |
||
294 | 'cc-0' => [ |
||
295 | 'url' => 'https://creativecommons.org/publicdomain/zero/1.0/', |
||
296 | 'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-0.png', |
||
297 | ], |
||
298 | 'gfdl' => [ |
||
299 | 'url' => 'https://www.gnu.org/copyleft/fdl.html', |
||
300 | 'icon' => '$wgResourceBasePath/resources/assets/licenses/gnu-fdl.png', |
||
301 | ], |
||
302 | 'none' => [ |
||
303 | 'url' => '', |
||
304 | 'icon' => '', |
||
305 | 'text' => '' |
||
306 | ], |
||
307 | 'cc-choose' => [ |
||
308 | // Details will be filled in by the selector. |
||
309 | 'url' => '', |
||
310 | 'icon' => '', |
||
311 | 'text' => '', |
||
312 | ], |
||
313 | ]; |
||
314 | |||
315 | /** |
||
316 | * URL to mediawiki-announce subscription |
||
317 | */ |
||
318 | protected $mediaWikiAnnounceUrl = |
||
319 | 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce'; |
||
320 | |||
321 | /** |
||
322 | * Supported language codes for Mailman |
||
323 | */ |
||
324 | protected $mediaWikiAnnounceLanguages = [ |
||
325 | 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu', |
||
326 | 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru', |
||
327 | 'sl', 'sr', 'sv', 'tr', 'uk' |
||
328 | ]; |
||
329 | |||
330 | /** |
||
331 | * UI interface for displaying a short message |
||
332 | * The parameters are like parameters to wfMessage(). |
||
333 | * The messages will be in wikitext format, which will be converted to an |
||
334 | * output format such as HTML or text before being sent to the user. |
||
335 | * @param string $msg |
||
336 | */ |
||
337 | abstract public function showMessage( $msg /*, ... */ ); |
||
338 | |||
339 | /** |
||
340 | * Same as showMessage(), but for displaying errors |
||
341 | * @param string $msg |
||
342 | */ |
||
343 | abstract public function showError( $msg /*, ... */ ); |
||
344 | |||
345 | /** |
||
346 | * Show a message to the installing user by using a Status object |
||
347 | * @param Status $status |
||
348 | */ |
||
349 | abstract public function showStatusMessage( Status $status ); |
||
350 | |||
351 | /** |
||
352 | * Constructs a Config object that contains configuration settings that should be |
||
353 | * overwritten for the installation process. |
||
354 | * |
||
355 | * @since 1.27 |
||
356 | * |
||
357 | * @param Config $baseConfig |
||
358 | * |
||
359 | * @return Config The config to use during installation. |
||
360 | */ |
||
361 | public static function getInstallerConfig( Config $baseConfig ) { |
||
362 | $configOverrides = new HashConfig(); |
||
363 | |||
364 | // disable (problematic) object cache types explicitly, preserving all other (working) ones |
||
365 | // bug T113843 |
||
366 | $emptyCache = [ 'class' => 'EmptyBagOStuff' ]; |
||
367 | |||
368 | $objectCaches = [ |
||
369 | CACHE_NONE => $emptyCache, |
||
370 | CACHE_DB => $emptyCache, |
||
371 | CACHE_ANYTHING => $emptyCache, |
||
372 | CACHE_MEMCACHED => $emptyCache, |
||
373 | ] + $baseConfig->get( 'ObjectCaches' ); |
||
374 | |||
375 | $configOverrides->set( 'ObjectCaches', $objectCaches ); |
||
376 | |||
377 | // Load the installer's i18n. |
||
378 | $messageDirs = $baseConfig->get( 'MessagesDirs' ); |
||
379 | $messageDirs['MediawikiInstaller'] = __DIR__ . '/i18n'; |
||
380 | |||
381 | $configOverrides->set( 'MessagesDirs', $messageDirs ); |
||
382 | |||
383 | $installerConfig = new MultiConfig( [ $configOverrides, $baseConfig ] ); |
||
384 | |||
385 | // make sure we use the installer config as the main config |
||
386 | $configRegistry = $baseConfig->get( 'ConfigRegistry' ); |
||
387 | $configRegistry['main'] = function() use ( $installerConfig ) { |
||
388 | return $installerConfig; |
||
389 | }; |
||
390 | |||
391 | $configOverrides->set( 'ConfigRegistry', $configRegistry ); |
||
392 | |||
393 | return $installerConfig; |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * Constructor, always call this from child classes. |
||
398 | */ |
||
399 | public function __construct() { |
||
0 ignored issues
–
show
|
|||
400 | global $wgMemc, $wgUser, $wgObjectCaches; |
||
401 | |||
402 | $defaultConfig = new GlobalVarConfig(); // all the stuff from DefaultSettings.php |
||
403 | $installerConfig = self::getInstallerConfig( $defaultConfig ); |
||
404 | |||
405 | // Reset all services and inject config overrides |
||
406 | MediaWiki\MediaWikiServices::resetGlobalInstance( $installerConfig ); |
||
407 | |||
408 | // Don't attempt to load user language options (T126177) |
||
409 | // This will be overridden in the web installer with the user-specified language |
||
410 | RequestContext::getMain()->setLanguage( 'en' ); |
||
411 | |||
412 | // Disable the i18n cache |
||
413 | // TODO: manage LocalisationCache singleton in MediaWikiServices |
||
414 | Language::getLocalisationCache()->disableBackend(); |
||
415 | |||
416 | // Disable all global services, since we don't have any configuration yet! |
||
417 | MediaWiki\MediaWikiServices::disableStorageBackend(); |
||
418 | |||
419 | // Disable object cache (otherwise CACHE_ANYTHING will try CACHE_DB and |
||
420 | // SqlBagOStuff will then throw since we just disabled wfGetDB) |
||
421 | $wgObjectCaches = MediaWikiServices::getInstance()->getMainConfig()->get( 'ObjectCaches' ); |
||
422 | $wgMemc = ObjectCache::getInstance( CACHE_NONE ); |
||
423 | |||
424 | // Having a user with id = 0 safeguards us from DB access via User::loadOptions(). |
||
425 | $wgUser = User::newFromId( 0 ); |
||
426 | RequestContext::getMain()->setUser( $wgUser ); |
||
427 | |||
428 | $this->settings = $this->internalDefaults; |
||
429 | |||
430 | foreach ( $this->defaultVarNames as $var ) { |
||
431 | $this->settings[$var] = $GLOBALS[$var]; |
||
432 | } |
||
433 | |||
434 | $this->doEnvironmentPreps(); |
||
435 | |||
436 | $this->compiledDBs = []; |
||
437 | foreach ( self::getDBTypes() as $type ) { |
||
438 | $installer = $this->getDBInstaller( $type ); |
||
439 | |||
440 | if ( !$installer->isCompiled() ) { |
||
441 | continue; |
||
442 | } |
||
443 | $this->compiledDBs[] = $type; |
||
444 | } |
||
445 | |||
446 | $this->parserTitle = Title::newFromText( 'Installer' ); |
||
447 | $this->parserOptions = new ParserOptions( $wgUser ); // language will be wrong :( |
||
448 | $this->parserOptions->setEditSection( false ); |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Get a list of known DB types. |
||
453 | * |
||
454 | * @return array |
||
455 | */ |
||
456 | public static function getDBTypes() { |
||
457 | return self::$dbTypes; |
||
458 | } |
||
459 | |||
460 | /** |
||
461 | * Do initial checks of the PHP environment. Set variables according to |
||
462 | * the observed environment. |
||
463 | * |
||
464 | * It's possible that this may be called under the CLI SAPI, not the SAPI |
||
465 | * that the wiki will primarily run under. In that case, the subclass should |
||
466 | * initialise variables such as wgScriptPath, before calling this function. |
||
467 | * |
||
468 | * Under the web subclass, it can already be assumed that PHP 5+ is in use |
||
469 | * and that sessions are working. |
||
470 | * |
||
471 | * @return Status |
||
472 | */ |
||
473 | public function doEnvironmentChecks() { |
||
474 | // Php version has already been checked by entry scripts |
||
475 | // Show message here for information purposes |
||
476 | if ( wfIsHHVM() ) { |
||
477 | $this->showMessage( 'config-env-hhvm', HHVM_VERSION ); |
||
478 | } else { |
||
479 | $this->showMessage( 'config-env-php', PHP_VERSION ); |
||
480 | } |
||
481 | |||
482 | $good = true; |
||
483 | // Must go here because an old version of PCRE can prevent other checks from completing |
||
484 | list( $pcreVersion ) = explode( ' ', PCRE_VERSION, 2 ); |
||
485 | if ( version_compare( $pcreVersion, self::MINIMUM_PCRE_VERSION, '<' ) ) { |
||
486 | $this->showError( 'config-pcre-old', self::MINIMUM_PCRE_VERSION, $pcreVersion ); |
||
487 | $good = false; |
||
488 | } else { |
||
489 | foreach ( $this->envChecks as $check ) { |
||
490 | $status = $this->$check(); |
||
491 | if ( $status === false ) { |
||
492 | $good = false; |
||
493 | } |
||
494 | } |
||
495 | } |
||
496 | |||
497 | $this->setVar( '_Environment', $good ); |
||
498 | |||
499 | return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' ); |
||
500 | } |
||
501 | |||
502 | public function doEnvironmentPreps() { |
||
503 | foreach ( $this->envPreps as $prep ) { |
||
504 | $this->$prep(); |
||
505 | } |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * Set a MW configuration variable, or internal installer configuration variable. |
||
510 | * |
||
511 | * @param string $name |
||
512 | * @param mixed $value |
||
513 | */ |
||
514 | public function setVar( $name, $value ) { |
||
515 | $this->settings[$name] = $value; |
||
516 | } |
||
517 | |||
518 | /** |
||
519 | * Get an MW configuration variable, or internal installer configuration variable. |
||
520 | * The defaults come from $GLOBALS (ultimately DefaultSettings.php). |
||
521 | * Installer variables are typically prefixed by an underscore. |
||
522 | * |
||
523 | * @param string $name |
||
524 | * @param mixed $default |
||
525 | * |
||
526 | * @return mixed |
||
527 | */ |
||
528 | View Code Duplication | public function getVar( $name, $default = null ) { |
|
529 | if ( !isset( $this->settings[$name] ) ) { |
||
530 | return $default; |
||
531 | } else { |
||
532 | return $this->settings[$name]; |
||
533 | } |
||
534 | } |
||
535 | |||
536 | /** |
||
537 | * Get a list of DBs supported by current PHP setup |
||
538 | * |
||
539 | * @return array |
||
540 | */ |
||
541 | public function getCompiledDBs() { |
||
542 | return $this->compiledDBs; |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * Get an instance of DatabaseInstaller for the specified DB type. |
||
547 | * |
||
548 | * @param mixed $type DB installer for which is needed, false to use default. |
||
549 | * |
||
550 | * @return DatabaseInstaller |
||
551 | */ |
||
552 | public function getDBInstaller( $type = false ) { |
||
553 | if ( !$type ) { |
||
554 | $type = $this->getVar( 'wgDBtype' ); |
||
555 | } |
||
556 | |||
557 | $type = strtolower( $type ); |
||
558 | |||
559 | if ( !isset( $this->dbInstallers[$type] ) ) { |
||
560 | $class = ucfirst( $type ) . 'Installer'; |
||
561 | $this->dbInstallers[$type] = new $class( $this ); |
||
562 | } |
||
563 | |||
564 | return $this->dbInstallers[$type]; |
||
565 | } |
||
566 | |||
567 | /** |
||
568 | * Determine if LocalSettings.php exists. If it does, return its variables. |
||
569 | * |
||
570 | * @return array |
||
571 | */ |
||
572 | public static function getExistingLocalSettings() { |
||
573 | global $IP; |
||
574 | |||
575 | // You might be wondering why this is here. Well if you don't do this |
||
576 | // then some poorly-formed extensions try to call their own classes |
||
577 | // after immediately registering them. We really need to get extension |
||
578 | // registration out of the global scope and into a real format. |
||
579 | // @see https://phabricator.wikimedia.org/T69440 |
||
580 | global $wgAutoloadClasses; |
||
581 | $wgAutoloadClasses = []; |
||
582 | |||
583 | // @codingStandardsIgnoreStart |
||
584 | // LocalSettings.php should not call functions, except wfLoadSkin/wfLoadExtensions |
||
585 | // Define the required globals here, to ensure, the functions can do it work correctly. |
||
586 | global $wgExtensionDirectory, $wgStyleDirectory; |
||
587 | // @codingStandardsIgnoreEnd |
||
588 | |||
589 | MediaWiki\suppressWarnings(); |
||
590 | $_lsExists = file_exists( "$IP/LocalSettings.php" ); |
||
591 | MediaWiki\restoreWarnings(); |
||
592 | |||
593 | if ( !$_lsExists ) { |
||
594 | return false; |
||
595 | } |
||
596 | unset( $_lsExists ); |
||
597 | |||
598 | require "$IP/includes/DefaultSettings.php"; |
||
599 | require "$IP/LocalSettings.php"; |
||
600 | |||
601 | return get_defined_vars(); |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Get a fake password for sending back to the user in HTML. |
||
606 | * This is a security mechanism to avoid compromise of the password in the |
||
607 | * event of session ID compromise. |
||
608 | * |
||
609 | * @param string $realPassword |
||
610 | * |
||
611 | * @return string |
||
612 | */ |
||
613 | public function getFakePassword( $realPassword ) { |
||
614 | return str_repeat( '*', strlen( $realPassword ) ); |
||
615 | } |
||
616 | |||
617 | /** |
||
618 | * Set a variable which stores a password, except if the new value is a |
||
619 | * fake password in which case leave it as it is. |
||
620 | * |
||
621 | * @param string $name |
||
622 | * @param mixed $value |
||
623 | */ |
||
624 | public function setPassword( $name, $value ) { |
||
625 | if ( !preg_match( '/^\*+$/', $value ) ) { |
||
626 | $this->setVar( $name, $value ); |
||
627 | } |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * On POSIX systems return the primary group of the webserver we're running under. |
||
632 | * On other systems just returns null. |
||
633 | * |
||
634 | * This is used to advice the user that he should chgrp his mw-config/data/images directory as the |
||
635 | * webserver user before he can install. |
||
636 | * |
||
637 | * Public because SqliteInstaller needs it, and doesn't subclass Installer. |
||
638 | * |
||
639 | * @return mixed |
||
640 | */ |
||
641 | public static function maybeGetWebserverPrimaryGroup() { |
||
642 | if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) { |
||
643 | # I don't know this, this isn't UNIX. |
||
644 | return null; |
||
645 | } |
||
646 | |||
647 | # posix_getegid() *not* getmygid() because we want the group of the webserver, |
||
648 | # not whoever owns the current script. |
||
649 | $gid = posix_getegid(); |
||
650 | $group = posix_getpwuid( $gid )['name']; |
||
651 | |||
652 | return $group; |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * Convert wikitext $text to HTML. |
||
657 | * |
||
658 | * This is potentially error prone since many parser features require a complete |
||
659 | * installed MW database. The solution is to just not use those features when you |
||
660 | * write your messages. This appears to work well enough. Basic formatting and |
||
661 | * external links work just fine. |
||
662 | * |
||
663 | * But in case a translator decides to throw in a "#ifexist" or internal link or |
||
664 | * whatever, this function is guarded to catch the attempted DB access and to present |
||
665 | * some fallback text. |
||
666 | * |
||
667 | * @param string $text |
||
668 | * @param bool $lineStart |
||
669 | * @return string |
||
670 | */ |
||
671 | public function parse( $text, $lineStart = false ) { |
||
672 | global $wgParser; |
||
673 | |||
674 | try { |
||
675 | $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart ); |
||
676 | $html = $out->getText(); |
||
677 | } catch ( DBAccessError $e ) { |
||
678 | $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text ); |
||
679 | |||
680 | if ( !empty( $this->debug ) ) { |
||
0 ignored issues
–
show
The property
debug does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
681 | $html .= "<!--\n" . $e->getTraceAsString() . "\n-->"; |
||
682 | } |
||
683 | } |
||
684 | |||
685 | return $html; |
||
686 | } |
||
687 | |||
688 | /** |
||
689 | * @return ParserOptions |
||
690 | */ |
||
691 | public function getParserOptions() { |
||
692 | return $this->parserOptions; |
||
693 | } |
||
694 | |||
695 | public function disableLinkPopups() { |
||
696 | $this->parserOptions->setExternalLinkTarget( false ); |
||
697 | } |
||
698 | |||
699 | public function restoreLinkPopups() { |
||
700 | global $wgExternalLinkTarget; |
||
701 | $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget ); |
||
702 | } |
||
703 | |||
704 | /** |
||
705 | * Install step which adds a row to the site_stats table with appropriate |
||
706 | * initial values. |
||
707 | * |
||
708 | * @param DatabaseInstaller $installer |
||
709 | * |
||
710 | * @return Status |
||
711 | */ |
||
712 | public function populateSiteStats( DatabaseInstaller $installer ) { |
||
713 | $status = $installer->getConnection(); |
||
714 | if ( !$status->isOK() ) { |
||
715 | return $status; |
||
716 | } |
||
717 | $status->value->insert( |
||
718 | 'site_stats', |
||
719 | [ |
||
720 | 'ss_row_id' => 1, |
||
721 | 'ss_total_edits' => 0, |
||
722 | 'ss_good_articles' => 0, |
||
723 | 'ss_total_pages' => 0, |
||
724 | 'ss_users' => 0, |
||
725 | 'ss_images' => 0 |
||
726 | ], |
||
727 | __METHOD__, 'IGNORE' |
||
728 | ); |
||
729 | |||
730 | return Status::newGood(); |
||
731 | } |
||
732 | |||
733 | /** |
||
734 | * Environment check for DB types. |
||
735 | * @return bool |
||
736 | */ |
||
737 | protected function envCheckDB() { |
||
738 | global $wgLang; |
||
739 | |||
740 | $allNames = []; |
||
741 | |||
742 | // Messages: config-type-mysql, config-type-postgres, config-type-oracle, |
||
743 | // config-type-sqlite |
||
744 | foreach ( self::getDBTypes() as $name ) { |
||
745 | $allNames[] = wfMessage( "config-type-$name" )->text(); |
||
746 | } |
||
747 | |||
748 | $databases = $this->getCompiledDBs(); |
||
749 | |||
750 | $databases = array_flip( $databases ); |
||
751 | foreach ( array_keys( $databases ) as $db ) { |
||
752 | $installer = $this->getDBInstaller( $db ); |
||
753 | $status = $installer->checkPrerequisites(); |
||
754 | if ( !$status->isGood() ) { |
||
755 | $this->showStatusMessage( $status ); |
||
756 | } |
||
757 | if ( !$status->isOK() ) { |
||
758 | unset( $databases[$db] ); |
||
759 | } |
||
760 | } |
||
761 | $databases = array_flip( $databases ); |
||
762 | if ( !$databases ) { |
||
763 | $this->showError( 'config-no-db', $wgLang->commaList( $allNames ), count( $allNames ) ); |
||
764 | |||
765 | // @todo FIXME: This only works for the web installer! |
||
766 | return false; |
||
767 | } |
||
768 | |||
769 | return true; |
||
770 | } |
||
771 | |||
772 | /** |
||
773 | * Some versions of libxml+PHP break < and > encoding horribly |
||
774 | * @return bool |
||
775 | */ |
||
776 | protected function envCheckBrokenXML() { |
||
777 | $test = new PhpXmlBugTester(); |
||
778 | if ( !$test->ok ) { |
||
779 | $this->showError( 'config-brokenlibxml' ); |
||
780 | |||
781 | return false; |
||
782 | } |
||
783 | |||
784 | return true; |
||
785 | } |
||
786 | |||
787 | /** |
||
788 | * Environment check for the PCRE module. |
||
789 | * |
||
790 | * @note If this check were to fail, the parser would |
||
791 | * probably throw an exception before the result |
||
792 | * of this check is shown to the user. |
||
793 | * @return bool |
||
794 | */ |
||
795 | protected function envCheckPCRE() { |
||
796 | MediaWiki\suppressWarnings(); |
||
797 | $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' ); |
||
798 | // Need to check for \p support too, as PCRE can be compiled |
||
799 | // with utf8 support, but not unicode property support. |
||
800 | // check that \p{Zs} (space separators) matches |
||
801 | // U+3000 (Ideographic space) |
||
802 | $regexprop = preg_replace( '/\p{Zs}/u', '', "-\xE3\x80\x80-" ); |
||
803 | MediaWiki\restoreWarnings(); |
||
804 | if ( $regexd != '--' || $regexprop != '--' ) { |
||
805 | $this->showError( 'config-pcre-no-utf8' ); |
||
806 | |||
807 | return false; |
||
808 | } |
||
809 | |||
810 | return true; |
||
811 | } |
||
812 | |||
813 | /** |
||
814 | * Environment check for available memory. |
||
815 | * @return bool |
||
816 | */ |
||
817 | protected function envCheckMemory() { |
||
818 | $limit = ini_get( 'memory_limit' ); |
||
819 | |||
820 | if ( !$limit || $limit == -1 ) { |
||
821 | return true; |
||
822 | } |
||
823 | |||
824 | $n = wfShorthandToInteger( $limit ); |
||
825 | |||
826 | if ( $n < $this->minMemorySize * 1024 * 1024 ) { |
||
827 | $newLimit = "{$this->minMemorySize}M"; |
||
828 | |||
829 | if ( ini_set( "memory_limit", $newLimit ) === false ) { |
||
830 | $this->showMessage( 'config-memory-bad', $limit ); |
||
831 | } else { |
||
832 | $this->showMessage( 'config-memory-raised', $limit, $newLimit ); |
||
833 | $this->setVar( '_RaiseMemory', true ); |
||
834 | } |
||
835 | } |
||
836 | |||
837 | return true; |
||
838 | } |
||
839 | |||
840 | /** |
||
841 | * Environment check for compiled object cache types. |
||
842 | */ |
||
843 | protected function envCheckCache() { |
||
844 | $caches = []; |
||
845 | foreach ( $this->objectCaches as $name => $function ) { |
||
846 | if ( function_exists( $function ) ) { |
||
847 | if ( $name == 'xcache' && !wfIniGetBool( 'xcache.var_size' ) ) { |
||
848 | continue; |
||
849 | } |
||
850 | $caches[$name] = true; |
||
851 | } |
||
852 | } |
||
853 | |||
854 | if ( !$caches ) { |
||
855 | $key = 'config-no-cache-apcu'; |
||
856 | $this->showMessage( $key ); |
||
857 | } |
||
858 | |||
859 | $this->setVar( '_Caches', $caches ); |
||
860 | } |
||
861 | |||
862 | /** |
||
863 | * Scare user to death if they have mod_security or mod_security2 |
||
864 | * @return bool |
||
865 | */ |
||
866 | protected function envCheckModSecurity() { |
||
867 | if ( self::apacheModulePresent( 'mod_security' ) |
||
868 | || self::apacheModulePresent( 'mod_security2' ) ) { |
||
869 | $this->showMessage( 'config-mod-security' ); |
||
870 | } |
||
871 | |||
872 | return true; |
||
873 | } |
||
874 | |||
875 | /** |
||
876 | * Search for GNU diff3. |
||
877 | * @return bool |
||
878 | */ |
||
879 | protected function envCheckDiff3() { |
||
880 | $names = [ "gdiff3", "diff3", "diff3.exe" ]; |
||
881 | $versionInfo = [ '$1 --version 2>&1', 'GNU diffutils' ]; |
||
882 | |||
883 | $diff3 = self::locateExecutableInDefaultPaths( $names, $versionInfo ); |
||
884 | |||
885 | if ( $diff3 ) { |
||
0 ignored issues
–
show
The expression
$diff3 of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
886 | $this->setVar( 'wgDiff3', $diff3 ); |
||
887 | } else { |
||
888 | $this->setVar( 'wgDiff3', false ); |
||
889 | $this->showMessage( 'config-diff3-bad' ); |
||
890 | } |
||
891 | |||
892 | return true; |
||
893 | } |
||
894 | |||
895 | /** |
||
896 | * Environment check for ImageMagick and GD. |
||
897 | * @return bool |
||
898 | */ |
||
899 | protected function envCheckGraphics() { |
||
900 | $names = [ wfIsWindows() ? 'convert.exe' : 'convert' ]; |
||
901 | $versionInfo = [ '$1 -version', 'ImageMagick' ]; |
||
902 | $convert = self::locateExecutableInDefaultPaths( $names, $versionInfo ); |
||
903 | |||
904 | $this->setVar( 'wgImageMagickConvertCommand', '' ); |
||
905 | if ( $convert ) { |
||
0 ignored issues
–
show
The expression
$convert of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
906 | $this->setVar( 'wgImageMagickConvertCommand', $convert ); |
||
907 | $this->showMessage( 'config-imagemagick', $convert ); |
||
908 | |||
909 | return true; |
||
910 | } elseif ( function_exists( 'imagejpeg' ) ) { |
||
911 | $this->showMessage( 'config-gd' ); |
||
912 | } else { |
||
913 | $this->showMessage( 'config-no-scaling' ); |
||
914 | } |
||
915 | |||
916 | return true; |
||
917 | } |
||
918 | |||
919 | /** |
||
920 | * Search for git. |
||
921 | * |
||
922 | * @since 1.22 |
||
923 | * @return bool |
||
924 | */ |
||
925 | protected function envCheckGit() { |
||
926 | $names = [ wfIsWindows() ? 'git.exe' : 'git' ]; |
||
927 | $versionInfo = [ '$1 --version', 'git version' ]; |
||
928 | |||
929 | $git = self::locateExecutableInDefaultPaths( $names, $versionInfo ); |
||
930 | |||
931 | if ( $git ) { |
||
0 ignored issues
–
show
The expression
$git of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
932 | $this->setVar( 'wgGitBin', $git ); |
||
933 | $this->showMessage( 'config-git', $git ); |
||
934 | } else { |
||
935 | $this->setVar( 'wgGitBin', false ); |
||
936 | $this->showMessage( 'config-git-bad' ); |
||
937 | } |
||
938 | |||
939 | return true; |
||
940 | } |
||
941 | |||
942 | /** |
||
943 | * Environment check to inform user which server we've assumed. |
||
944 | * |
||
945 | * @return bool |
||
946 | */ |
||
947 | protected function envCheckServer() { |
||
948 | $server = $this->envGetDefaultServer(); |
||
949 | if ( $server !== null ) { |
||
950 | $this->showMessage( 'config-using-server', $server ); |
||
951 | } |
||
952 | return true; |
||
953 | } |
||
954 | |||
955 | /** |
||
956 | * Environment check to inform user which paths we've assumed. |
||
957 | * |
||
958 | * @return bool |
||
959 | */ |
||
960 | protected function envCheckPath() { |
||
961 | $this->showMessage( |
||
962 | 'config-using-uri', |
||
963 | $this->getVar( 'wgServer' ), |
||
964 | $this->getVar( 'wgScriptPath' ) |
||
965 | ); |
||
966 | return true; |
||
967 | } |
||
968 | |||
969 | /** |
||
970 | * Environment check for preferred locale in shell |
||
971 | * @return bool |
||
972 | */ |
||
973 | protected function envCheckShellLocale() { |
||
974 | $os = php_uname( 's' ); |
||
975 | $supported = [ 'Linux', 'SunOS', 'HP-UX', 'Darwin' ]; # Tested these |
||
976 | |||
977 | if ( !in_array( $os, $supported ) ) { |
||
978 | return true; |
||
979 | } |
||
980 | |||
981 | # Get a list of available locales. |
||
982 | $ret = false; |
||
983 | $lines = wfShellExec( '/usr/bin/locale -a', $ret ); |
||
984 | |||
985 | if ( $ret ) { |
||
986 | return true; |
||
987 | } |
||
988 | |||
989 | $lines = array_map( 'trim', explode( "\n", $lines ) ); |
||
990 | $candidatesByLocale = []; |
||
991 | $candidatesByLang = []; |
||
992 | |||
993 | foreach ( $lines as $line ) { |
||
994 | if ( $line === '' ) { |
||
995 | continue; |
||
996 | } |
||
997 | |||
998 | if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) { |
||
999 | continue; |
||
1000 | } |
||
1001 | |||
1002 | list( , $lang, , , ) = $m; |
||
1003 | |||
1004 | $candidatesByLocale[$m[0]] = $m; |
||
1005 | $candidatesByLang[$lang][] = $m; |
||
1006 | } |
||
1007 | |||
1008 | # Try the current value of LANG. |
||
1009 | if ( isset( $candidatesByLocale[getenv( 'LANG' )] ) ) { |
||
1010 | $this->setVar( 'wgShellLocale', getenv( 'LANG' ) ); |
||
1011 | |||
1012 | return true; |
||
1013 | } |
||
1014 | |||
1015 | # Try the most common ones. |
||
1016 | $commonLocales = [ 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' ]; |
||
1017 | foreach ( $commonLocales as $commonLocale ) { |
||
1018 | if ( isset( $candidatesByLocale[$commonLocale] ) ) { |
||
1019 | $this->setVar( 'wgShellLocale', $commonLocale ); |
||
1020 | |||
1021 | return true; |
||
1022 | } |
||
1023 | } |
||
1024 | |||
1025 | # Is there an available locale in the Wiki's language? |
||
1026 | $wikiLang = $this->getVar( 'wgLanguageCode' ); |
||
1027 | |||
1028 | if ( isset( $candidatesByLang[$wikiLang] ) ) { |
||
1029 | $m = reset( $candidatesByLang[$wikiLang] ); |
||
1030 | $this->setVar( 'wgShellLocale', $m[0] ); |
||
1031 | |||
1032 | return true; |
||
1033 | } |
||
1034 | |||
1035 | # Are there any at all? |
||
1036 | if ( count( $candidatesByLocale ) ) { |
||
1037 | $m = reset( $candidatesByLocale ); |
||
1038 | $this->setVar( 'wgShellLocale', $m[0] ); |
||
1039 | |||
1040 | return true; |
||
1041 | } |
||
1042 | |||
1043 | # Give up. |
||
1044 | return true; |
||
1045 | } |
||
1046 | |||
1047 | /** |
||
1048 | * Environment check for the permissions of the uploads directory |
||
1049 | * @return bool |
||
1050 | */ |
||
1051 | protected function envCheckUploadsDirectory() { |
||
1052 | global $IP; |
||
1053 | |||
1054 | $dir = $IP . '/images/'; |
||
1055 | $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/'; |
||
1056 | $safe = !$this->dirIsExecutable( $dir, $url ); |
||
1057 | |||
1058 | if ( !$safe ) { |
||
1059 | $this->showMessage( 'config-uploads-not-safe', $dir ); |
||
1060 | } |
||
1061 | |||
1062 | return true; |
||
1063 | } |
||
1064 | |||
1065 | /** |
||
1066 | * Checks if suhosin.get.max_value_length is set, and if so generate |
||
1067 | * a warning because it decreases ResourceLoader performance. |
||
1068 | * @return bool |
||
1069 | */ |
||
1070 | protected function envCheckSuhosinMaxValueLength() { |
||
1071 | $maxValueLength = ini_get( 'suhosin.get.max_value_length' ); |
||
1072 | if ( $maxValueLength > 0 && $maxValueLength < 1024 ) { |
||
1073 | // Only warn if the value is below the sane 1024 |
||
1074 | $this->showMessage( 'config-suhosin-max-value-length', $maxValueLength ); |
||
1075 | } |
||
1076 | |||
1077 | return true; |
||
1078 | } |
||
1079 | |||
1080 | /** |
||
1081 | * Convert a hex string representing a Unicode code point to that code point. |
||
1082 | * @param string $c |
||
1083 | * @return string |
||
1084 | */ |
||
1085 | protected function unicodeChar( $c ) { |
||
1086 | $c = hexdec( $c ); |
||
1087 | if ( $c <= 0x7F ) { |
||
1088 | return chr( $c ); |
||
1089 | } elseif ( $c <= 0x7FF ) { |
||
1090 | return chr( 0xC0 | $c >> 6 ) . chr( 0x80 | $c & 0x3F ); |
||
1091 | } elseif ( $c <= 0xFFFF ) { |
||
1092 | return chr( 0xE0 | $c >> 12 ) . chr( 0x80 | $c >> 6 & 0x3F ) . |
||
1093 | chr( 0x80 | $c & 0x3F ); |
||
1094 | } elseif ( $c <= 0x10FFFF ) { |
||
1095 | return chr( 0xF0 | $c >> 18 ) . chr( 0x80 | $c >> 12 & 0x3F ) . |
||
1096 | chr( 0x80 | $c >> 6 & 0x3F ) . |
||
1097 | chr( 0x80 | $c & 0x3F ); |
||
1098 | } else { |
||
1099 | return false; |
||
1100 | } |
||
1101 | } |
||
1102 | |||
1103 | /** |
||
1104 | * Check the libicu version |
||
1105 | */ |
||
1106 | protected function envCheckLibicu() { |
||
1107 | /** |
||
1108 | * This needs to be updated something that the latest libicu |
||
1109 | * will properly normalize. This normalization was found at |
||
1110 | * http://www.unicode.org/versions/Unicode5.2.0/#Character_Additions |
||
1111 | * Note that we use the hex representation to create the code |
||
1112 | * points in order to avoid any Unicode-destroying during transit. |
||
1113 | */ |
||
1114 | $not_normal_c = $this->unicodeChar( "FA6C" ); |
||
1115 | $normal_c = $this->unicodeChar( "242EE" ); |
||
1116 | |||
1117 | $useNormalizer = 'php'; |
||
1118 | $needsUpdate = false; |
||
1119 | |||
1120 | if ( function_exists( 'normalizer_normalize' ) ) { |
||
1121 | $useNormalizer = 'intl'; |
||
1122 | $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C ); |
||
1123 | if ( $intl !== $normal_c ) { |
||
1124 | $needsUpdate = true; |
||
1125 | } |
||
1126 | } |
||
1127 | |||
1128 | // Uses messages 'config-unicode-using-php' and 'config-unicode-using-intl' |
||
1129 | if ( $useNormalizer === 'php' ) { |
||
1130 | $this->showMessage( 'config-unicode-pure-php-warning' ); |
||
1131 | } else { |
||
1132 | $this->showMessage( 'config-unicode-using-' . $useNormalizer ); |
||
1133 | if ( $needsUpdate ) { |
||
1134 | $this->showMessage( 'config-unicode-update-warning' ); |
||
1135 | } |
||
1136 | } |
||
1137 | } |
||
1138 | |||
1139 | /** |
||
1140 | * Environment prep for the server hostname. |
||
1141 | */ |
||
1142 | protected function envPrepServer() { |
||
1143 | $server = $this->envGetDefaultServer(); |
||
1144 | if ( $server !== null ) { |
||
1145 | $this->setVar( 'wgServer', $server ); |
||
1146 | } |
||
1147 | } |
||
1148 | |||
1149 | /** |
||
1150 | * Helper function to be called from envPrepServer() |
||
1151 | * @return string |
||
1152 | */ |
||
1153 | abstract protected function envGetDefaultServer(); |
||
1154 | |||
1155 | /** |
||
1156 | * Environment prep for setting $IP and $wgScriptPath. |
||
1157 | */ |
||
1158 | protected function envPrepPath() { |
||
1159 | global $IP; |
||
1160 | $IP = dirname( dirname( __DIR__ ) ); |
||
1161 | $this->setVar( 'IP', $IP ); |
||
1162 | } |
||
1163 | |||
1164 | /** |
||
1165 | * Get an array of likely places we can find executables. Check a bunch |
||
1166 | * of known Unix-like defaults, as well as the PATH environment variable |
||
1167 | * (which should maybe make it work for Windows?) |
||
1168 | * |
||
1169 | * @return array |
||
1170 | */ |
||
1171 | protected static function getPossibleBinPaths() { |
||
1172 | return array_merge( |
||
1173 | [ '/usr/bin', '/usr/local/bin', '/opt/csw/bin', |
||
1174 | '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ], |
||
1175 | explode( PATH_SEPARATOR, getenv( 'PATH' ) ) |
||
1176 | ); |
||
1177 | } |
||
1178 | |||
1179 | /** |
||
1180 | * Search a path for any of the given executable names. Returns the |
||
1181 | * executable name if found. Also checks the version string returned |
||
1182 | * by each executable. |
||
1183 | * |
||
1184 | * Used only by environment checks. |
||
1185 | * |
||
1186 | * @param string $path Path to search |
||
1187 | * @param array $names Array of executable names |
||
1188 | * @param array|bool $versionInfo False or array with two members: |
||
1189 | * 0 => Command to run for version check, with $1 for the full executable name |
||
1190 | * 1 => String to compare the output with |
||
1191 | * |
||
1192 | * If $versionInfo is not false, only executables with a version |
||
1193 | * matching $versionInfo[1] will be returned. |
||
1194 | * @return bool|string |
||
1195 | */ |
||
1196 | public static function locateExecutable( $path, $names, $versionInfo = false ) { |
||
1197 | if ( !is_array( $names ) ) { |
||
1198 | $names = [ $names ]; |
||
1199 | } |
||
1200 | |||
1201 | foreach ( $names as $name ) { |
||
1202 | $command = $path . DIRECTORY_SEPARATOR . $name; |
||
1203 | |||
1204 | MediaWiki\suppressWarnings(); |
||
1205 | $file_exists = is_executable( $command ); |
||
1206 | MediaWiki\restoreWarnings(); |
||
1207 | |||
1208 | if ( $file_exists ) { |
||
1209 | if ( !$versionInfo ) { |
||
1210 | return $command; |
||
1211 | } |
||
1212 | |||
1213 | $file = str_replace( '$1', wfEscapeShellArg( $command ), $versionInfo[0] ); |
||
1214 | if ( strstr( wfShellExec( $file ), $versionInfo[1] ) !== false ) { |
||
1215 | return $command; |
||
1216 | } |
||
1217 | } |
||
1218 | } |
||
1219 | |||
1220 | return false; |
||
1221 | } |
||
1222 | |||
1223 | /** |
||
1224 | * Same as locateExecutable(), but checks in getPossibleBinPaths() by default |
||
1225 | * @see locateExecutable() |
||
1226 | * @param array $names Array of possible names. |
||
1227 | * @param array|bool $versionInfo Default: false or array with two members: |
||
1228 | * 0 => Command to run for version check, with $1 for the full executable name |
||
1229 | * 1 => String to compare the output with |
||
1230 | * |
||
1231 | * If $versionInfo is not false, only executables with a version |
||
1232 | * matching $versionInfo[1] will be returned. |
||
1233 | * @return bool|string |
||
1234 | */ |
||
1235 | public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) { |
||
1236 | foreach ( self::getPossibleBinPaths() as $path ) { |
||
1237 | $exe = self::locateExecutable( $path, $names, $versionInfo ); |
||
1238 | if ( $exe !== false ) { |
||
1239 | return $exe; |
||
1240 | } |
||
1241 | } |
||
1242 | |||
1243 | return false; |
||
1244 | } |
||
1245 | |||
1246 | /** |
||
1247 | * Checks if scripts located in the given directory can be executed via the given URL. |
||
1248 | * |
||
1249 | * Used only by environment checks. |
||
1250 | * @param string $dir |
||
1251 | * @param string $url |
||
1252 | * @return bool|int|string |
||
1253 | */ |
||
1254 | public function dirIsExecutable( $dir, $url ) { |
||
1255 | $scriptTypes = [ |
||
1256 | 'php' => [ |
||
1257 | "<?php echo 'ex' . 'ec';", |
||
1258 | "#!/var/env php5\n<?php echo 'ex' . 'ec';", |
||
1259 | ], |
||
1260 | ]; |
||
1261 | |||
1262 | // it would be good to check other popular languages here, but it'll be slow. |
||
1263 | |||
1264 | MediaWiki\suppressWarnings(); |
||
1265 | |||
1266 | foreach ( $scriptTypes as $ext => $contents ) { |
||
1267 | foreach ( $contents as $source ) { |
||
1268 | $file = 'exectest.' . $ext; |
||
1269 | |||
1270 | if ( !file_put_contents( $dir . $file, $source ) ) { |
||
1271 | break; |
||
1272 | } |
||
1273 | |||
1274 | try { |
||
1275 | $text = Http::get( $url . $file, [ 'timeout' => 3 ], __METHOD__ ); |
||
1276 | } catch ( Exception $e ) { |
||
1277 | // Http::get throws with allow_url_fopen = false and no curl extension. |
||
1278 | $text = null; |
||
1279 | } |
||
1280 | unlink( $dir . $file ); |
||
1281 | |||
1282 | if ( $text == 'exec' ) { |
||
1283 | MediaWiki\restoreWarnings(); |
||
1284 | |||
1285 | return $ext; |
||
1286 | } |
||
1287 | } |
||
1288 | } |
||
1289 | |||
1290 | MediaWiki\restoreWarnings(); |
||
1291 | |||
1292 | return false; |
||
1293 | } |
||
1294 | |||
1295 | /** |
||
1296 | * Checks for presence of an Apache module. Works only if PHP is running as an Apache module, too. |
||
1297 | * |
||
1298 | * @param string $moduleName Name of module to check. |
||
1299 | * @return bool |
||
1300 | */ |
||
1301 | public static function apacheModulePresent( $moduleName ) { |
||
1302 | if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) { |
||
1303 | return true; |
||
1304 | } |
||
1305 | // try it the hard way |
||
1306 | ob_start(); |
||
1307 | phpinfo( INFO_MODULES ); |
||
1308 | $info = ob_get_clean(); |
||
1309 | |||
1310 | return strpos( $info, $moduleName ) !== false; |
||
1311 | } |
||
1312 | |||
1313 | /** |
||
1314 | * ParserOptions are constructed before we determined the language, so fix it |
||
1315 | * |
||
1316 | * @param Language $lang |
||
1317 | */ |
||
1318 | public function setParserLanguage( $lang ) { |
||
1319 | $this->parserOptions->setTargetLanguage( $lang ); |
||
1320 | $this->parserOptions->setUserLang( $lang ); |
||
1321 | } |
||
1322 | |||
1323 | /** |
||
1324 | * Overridden by WebInstaller to provide lastPage parameters. |
||
1325 | * @param string $page |
||
1326 | * @return string |
||
1327 | */ |
||
1328 | protected function getDocUrl( $page ) { |
||
0 ignored issues
–
show
getDocUrl uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1329 | return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page ); |
||
1330 | } |
||
1331 | |||
1332 | /** |
||
1333 | * Finds extensions that follow the format /$directory/Name/Name.php, |
||
1334 | * and returns an array containing the value for 'Name' for each found extension. |
||
1335 | * |
||
1336 | * Reasonable values for $directory include 'extensions' (the default) and 'skins'. |
||
1337 | * |
||
1338 | * @param string $directory Directory to search in |
||
1339 | * @return array |
||
1340 | */ |
||
1341 | public function findExtensions( $directory = 'extensions' ) { |
||
1342 | if ( $this->getVar( 'IP' ) === null ) { |
||
1343 | return []; |
||
1344 | } |
||
1345 | |||
1346 | $extDir = $this->getVar( 'IP' ) . '/' . $directory; |
||
1347 | if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) { |
||
1348 | return []; |
||
1349 | } |
||
1350 | |||
1351 | // extensions -> extension.json, skins -> skin.json |
||
1352 | $jsonFile = substr( $directory, 0, strlen( $directory ) -1 ) . '.json'; |
||
1353 | |||
1354 | $dh = opendir( $extDir ); |
||
1355 | $exts = []; |
||
1356 | while ( ( $file = readdir( $dh ) ) !== false ) { |
||
1357 | if ( !is_dir( "$extDir/$file" ) ) { |
||
1358 | continue; |
||
1359 | } |
||
1360 | if ( file_exists( "$extDir/$file/$jsonFile" ) || file_exists( "$extDir/$file/$file.php" ) ) { |
||
1361 | $exts[] = $file; |
||
1362 | } |
||
1363 | } |
||
1364 | closedir( $dh ); |
||
1365 | natcasesort( $exts ); |
||
1366 | |||
1367 | return $exts; |
||
1368 | } |
||
1369 | |||
1370 | /** |
||
1371 | * Returns a default value to be used for $wgDefaultSkin: normally the one set in DefaultSettings, |
||
1372 | * but will fall back to another if the default skin is missing and some other one is present |
||
1373 | * instead. |
||
1374 | * |
||
1375 | * @param string[] $skinNames Names of installed skins. |
||
1376 | * @return string |
||
1377 | */ |
||
1378 | public function getDefaultSkin( array $skinNames ) { |
||
0 ignored issues
–
show
getDefaultSkin uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1379 | $defaultSkin = $GLOBALS['wgDefaultSkin']; |
||
1380 | if ( !$skinNames || in_array( $defaultSkin, $skinNames ) ) { |
||
1381 | return $defaultSkin; |
||
1382 | } else { |
||
1383 | return $skinNames[0]; |
||
1384 | } |
||
1385 | } |
||
1386 | |||
1387 | /** |
||
1388 | * Installs the auto-detected extensions. |
||
1389 | * |
||
1390 | * @return Status |
||
1391 | */ |
||
1392 | protected function includeExtensions() { |
||
0 ignored issues
–
show
includeExtensions uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1393 | global $IP; |
||
1394 | $exts = $this->getVar( '_Extensions' ); |
||
1395 | $IP = $this->getVar( 'IP' ); |
||
1396 | |||
1397 | /** |
||
1398 | * We need to include DefaultSettings before including extensions to avoid |
||
1399 | * warnings about unset variables. However, the only thing we really |
||
1400 | * want here is $wgHooks['LoadExtensionSchemaUpdates']. This won't work |
||
1401 | * if the extension has hidden hook registration in $wgExtensionFunctions, |
||
1402 | * but we're not opening that can of worms |
||
1403 | * @see https://phabricator.wikimedia.org/T28857 |
||
1404 | */ |
||
1405 | global $wgAutoloadClasses; |
||
1406 | $wgAutoloadClasses = []; |
||
1407 | $queue = []; |
||
1408 | |||
1409 | require "$IP/includes/DefaultSettings.php"; |
||
1410 | |||
1411 | foreach ( $exts as $e ) { |
||
1412 | if ( file_exists( "$IP/extensions/$e/extension.json" ) ) { |
||
1413 | $queue["$IP/extensions/$e/extension.json"] = 1; |
||
1414 | } else { |
||
1415 | require_once "$IP/extensions/$e/$e.php"; |
||
1416 | } |
||
1417 | } |
||
1418 | |||
1419 | $registry = new ExtensionRegistry(); |
||
1420 | $data = $registry->readFromQueue( $queue ); |
||
1421 | $wgAutoloadClasses += $data['autoload']; |
||
1422 | |||
1423 | $hooksWeWant = isset( $wgHooks['LoadExtensionSchemaUpdates'] ) ? |
||
0 ignored issues
–
show
The variable
$wgHooks seems to never exist, and therefore isset should always return false . Did you maybe rename this variable?
This check looks for calls to This is most likely caused by the renaming of a variable or the removal of a function/method parameter. ![]() |
|||
1424 | $wgHooks['LoadExtensionSchemaUpdates'] : []; |
||
1425 | |||
1426 | View Code Duplication | if ( isset( $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] ) ) { |
|
1427 | $hooksWeWant = array_merge_recursive( |
||
1428 | $hooksWeWant, |
||
1429 | $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] |
||
1430 | ); |
||
1431 | } |
||
1432 | // Unset everyone else's hooks. Lord knows what someone might be doing |
||
1433 | // in ParserFirstCallInit (see bug 27171) |
||
1434 | $GLOBALS['wgHooks'] = [ 'LoadExtensionSchemaUpdates' => $hooksWeWant ]; |
||
1435 | |||
1436 | return Status::newGood(); |
||
1437 | } |
||
1438 | |||
1439 | /** |
||
1440 | * Get an array of install steps. Should always be in the format of |
||
1441 | * [ |
||
1442 | * 'name' => 'someuniquename', |
||
1443 | * 'callback' => [ $obj, 'method' ], |
||
1444 | * ] |
||
1445 | * There must be a config-install-$name message defined per step, which will |
||
1446 | * be shown on install. |
||
1447 | * |
||
1448 | * @param DatabaseInstaller $installer DatabaseInstaller so we can make callbacks |
||
1449 | * @return array |
||
1450 | */ |
||
1451 | protected function getInstallSteps( DatabaseInstaller $installer ) { |
||
1452 | $coreInstallSteps = [ |
||
1453 | [ 'name' => 'database', 'callback' => [ $installer, 'setupDatabase' ] ], |
||
1454 | [ 'name' => 'tables', 'callback' => [ $installer, 'createTables' ] ], |
||
1455 | [ 'name' => 'interwiki', 'callback' => [ $installer, 'populateInterwikiTable' ] ], |
||
1456 | [ 'name' => 'stats', 'callback' => [ $this, 'populateSiteStats' ] ], |
||
1457 | [ 'name' => 'keys', 'callback' => [ $this, 'generateKeys' ] ], |
||
1458 | [ 'name' => 'updates', 'callback' => [ $installer, 'insertUpdateKeys' ] ], |
||
1459 | [ 'name' => 'sysop', 'callback' => [ $this, 'createSysop' ] ], |
||
1460 | [ 'name' => 'mainpage', 'callback' => [ $this, 'createMainpage' ] ], |
||
1461 | ]; |
||
1462 | |||
1463 | // Build the array of install steps starting from the core install list, |
||
1464 | // then adding any callbacks that wanted to attach after a given step |
||
1465 | foreach ( $coreInstallSteps as $step ) { |
||
1466 | $this->installSteps[] = $step; |
||
1467 | View Code Duplication | if ( isset( $this->extraInstallSteps[$step['name']] ) ) { |
|
1468 | $this->installSteps = array_merge( |
||
1469 | $this->installSteps, |
||
1470 | $this->extraInstallSteps[$step['name']] |
||
1471 | ); |
||
1472 | } |
||
1473 | } |
||
1474 | |||
1475 | // Prepend any steps that want to be at the beginning |
||
1476 | View Code Duplication | if ( isset( $this->extraInstallSteps['BEGINNING'] ) ) { |
|
1477 | $this->installSteps = array_merge( |
||
1478 | $this->extraInstallSteps['BEGINNING'], |
||
1479 | $this->installSteps |
||
1480 | ); |
||
1481 | } |
||
1482 | |||
1483 | // Extensions should always go first, chance to tie into hooks and such |
||
1484 | if ( count( $this->getVar( '_Extensions' ) ) ) { |
||
1485 | array_unshift( $this->installSteps, |
||
1486 | [ 'name' => 'extensions', 'callback' => [ $this, 'includeExtensions' ] ] |
||
1487 | ); |
||
1488 | $this->installSteps[] = [ |
||
1489 | 'name' => 'extension-tables', |
||
1490 | 'callback' => [ $installer, 'createExtensionTables' ] |
||
1491 | ]; |
||
1492 | } |
||
1493 | |||
1494 | return $this->installSteps; |
||
1495 | } |
||
1496 | |||
1497 | /** |
||
1498 | * Actually perform the installation. |
||
1499 | * |
||
1500 | * @param callable $startCB A callback array for the beginning of each step |
||
1501 | * @param callable $endCB A callback array for the end of each step |
||
1502 | * |
||
1503 | * @return array Array of Status objects |
||
1504 | */ |
||
1505 | public function performInstallation( $startCB, $endCB ) { |
||
1506 | $installResults = []; |
||
1507 | $installer = $this->getDBInstaller(); |
||
1508 | $installer->preInstall(); |
||
1509 | $steps = $this->getInstallSteps( $installer ); |
||
1510 | foreach ( $steps as $stepObj ) { |
||
1511 | $name = $stepObj['name']; |
||
1512 | call_user_func_array( $startCB, [ $name ] ); |
||
1513 | |||
1514 | // Perform the callback step |
||
1515 | $status = call_user_func( $stepObj['callback'], $installer ); |
||
1516 | |||
1517 | // Output and save the results |
||
1518 | call_user_func( $endCB, $name, $status ); |
||
1519 | $installResults[$name] = $status; |
||
1520 | |||
1521 | // If we've hit some sort of fatal, we need to bail. |
||
1522 | // Callback already had a chance to do output above. |
||
1523 | if ( !$status->isOk() ) { |
||
1524 | break; |
||
1525 | } |
||
1526 | } |
||
1527 | if ( $status->isOk() ) { |
||
0 ignored issues
–
show
The variable
$status does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1528 | $this->setVar( '_InstallDone', true ); |
||
1529 | } |
||
1530 | |||
1531 | return $installResults; |
||
1532 | } |
||
1533 | |||
1534 | /** |
||
1535 | * Generate $wgSecretKey. Will warn if we had to use an insecure random source. |
||
1536 | * |
||
1537 | * @return Status |
||
1538 | */ |
||
1539 | public function generateKeys() { |
||
1540 | $keys = [ 'wgSecretKey' => 64 ]; |
||
1541 | if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) { |
||
1542 | $keys['wgUpgradeKey'] = 16; |
||
1543 | } |
||
1544 | |||
1545 | return $this->doGenerateKeys( $keys ); |
||
1546 | } |
||
1547 | |||
1548 | /** |
||
1549 | * Generate a secret value for variables using our CryptRand generator. |
||
1550 | * Produce a warning if the random source was insecure. |
||
1551 | * |
||
1552 | * @param array $keys |
||
1553 | * @return Status |
||
1554 | */ |
||
1555 | protected function doGenerateKeys( $keys ) { |
||
1556 | $status = Status::newGood(); |
||
1557 | |||
1558 | $strong = true; |
||
1559 | foreach ( $keys as $name => $length ) { |
||
1560 | $secretKey = MWCryptRand::generateHex( $length, true ); |
||
1561 | if ( !MWCryptRand::wasStrong() ) { |
||
1562 | $strong = false; |
||
1563 | } |
||
1564 | |||
1565 | $this->setVar( $name, $secretKey ); |
||
1566 | } |
||
1567 | |||
1568 | if ( !$strong ) { |
||
1569 | $names = array_keys( $keys ); |
||
1570 | $names = preg_replace( '/^(.*)$/', '\$$1', $names ); |
||
1571 | global $wgLang; |
||
1572 | $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) ); |
||
1573 | } |
||
1574 | |||
1575 | return $status; |
||
1576 | } |
||
1577 | |||
1578 | /** |
||
1579 | * Create the first user account, grant it sysop and bureaucrat rights |
||
1580 | * |
||
1581 | * @return Status |
||
1582 | */ |
||
1583 | protected function createSysop() { |
||
1584 | $name = $this->getVar( '_AdminName' ); |
||
1585 | $user = User::newFromName( $name ); |
||
1586 | |||
1587 | if ( !$user ) { |
||
1588 | // We should've validated this earlier anyway! |
||
1589 | return Status::newFatal( 'config-admin-error-user', $name ); |
||
1590 | } |
||
1591 | |||
1592 | if ( $user->idForName() == 0 ) { |
||
1593 | $user->addToDatabase(); |
||
1594 | |||
1595 | try { |
||
1596 | $user->setPassword( $this->getVar( '_AdminPassword' ) ); |
||
1597 | } catch ( PasswordError $pwe ) { |
||
1598 | return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() ); |
||
1599 | } |
||
1600 | |||
1601 | $user->addGroup( 'sysop' ); |
||
1602 | $user->addGroup( 'bureaucrat' ); |
||
1603 | if ( $this->getVar( '_AdminEmail' ) ) { |
||
1604 | $user->setEmail( $this->getVar( '_AdminEmail' ) ); |
||
1605 | } |
||
1606 | $user->saveSettings(); |
||
1607 | |||
1608 | // Update user count |
||
1609 | $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); |
||
1610 | $ssUpdate->doUpdate(); |
||
1611 | } |
||
1612 | $status = Status::newGood(); |
||
1613 | |||
1614 | if ( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) { |
||
1615 | $this->subscribeToMediaWikiAnnounce( $status ); |
||
1616 | } |
||
1617 | |||
1618 | return $status; |
||
1619 | } |
||
1620 | |||
1621 | /** |
||
1622 | * @param Status $s |
||
1623 | */ |
||
1624 | private function subscribeToMediaWikiAnnounce( Status $s ) { |
||
1625 | $params = [ |
||
1626 | 'email' => $this->getVar( '_AdminEmail' ), |
||
1627 | 'language' => 'en', |
||
1628 | 'digest' => 0 |
||
1629 | ]; |
||
1630 | |||
1631 | // Mailman doesn't support as many languages as we do, so check to make |
||
1632 | // sure their selected language is available |
||
1633 | $myLang = $this->getVar( '_UserLang' ); |
||
1634 | if ( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) { |
||
1635 | $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR |
||
1636 | $params['language'] = $myLang; |
||
1637 | } |
||
1638 | |||
1639 | if ( MWHttpRequest::canMakeRequests() ) { |
||
1640 | $res = MWHttpRequest::factory( $this->mediaWikiAnnounceUrl, |
||
1641 | [ 'method' => 'POST', 'postData' => $params ], __METHOD__ )->execute(); |
||
1642 | if ( !$res->isOK() ) { |
||
1643 | $s->warning( 'config-install-subscribe-fail', $res->getMessage() ); |
||
1644 | } |
||
1645 | } else { |
||
1646 | $s->warning( 'config-install-subscribe-notpossible' ); |
||
1647 | } |
||
1648 | } |
||
1649 | |||
1650 | /** |
||
1651 | * Insert Main Page with default content. |
||
1652 | * |
||
1653 | * @param DatabaseInstaller $installer |
||
1654 | * @return Status |
||
1655 | */ |
||
1656 | protected function createMainpage( DatabaseInstaller $installer ) { |
||
1657 | $status = Status::newGood(); |
||
1658 | try { |
||
1659 | $page = WikiPage::factory( Title::newMainPage() ); |
||
0 ignored issues
–
show
It seems like
\Title::newMainPage() can be null ; however, factory() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
1660 | $content = new WikitextContent( |
||
1661 | wfMessage( 'mainpagetext' )->inContentLanguage()->text() . "\n\n" . |
||
1662 | wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text() |
||
1663 | ); |
||
1664 | |||
1665 | $status = $page->doEditContent( $content, |
||
1666 | '', |
||
1667 | EDIT_NEW, |
||
1668 | false, |
||
1669 | User::newFromName( 'MediaWiki default' ) |
||
0 ignored issues
–
show
|
|||
1670 | ); |
||
1671 | } catch ( Exception $e ) { |
||
1672 | // using raw, because $wgShowExceptionDetails can not be set yet |
||
1673 | $status->fatal( 'config-install-mainpage-failed', $e->getMessage() ); |
||
1674 | } |
||
1675 | |||
1676 | return $status; |
||
1677 | } |
||
1678 | |||
1679 | /** |
||
1680 | * Override the necessary bits of the config to run an installation. |
||
1681 | */ |
||
1682 | public static function overrideConfig() { |
||
0 ignored issues
–
show
overrideConfig uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1683 | // Use PHP's built-in session handling, since MediaWiki's |
||
1684 | // SessionHandler can't work before we have an object cache set up. |
||
1685 | define( 'MW_NO_SESSION_HANDLER', 1 ); |
||
1686 | |||
1687 | // Don't access the database |
||
1688 | $GLOBALS['wgUseDatabaseMessages'] = false; |
||
1689 | // Don't cache langconv tables |
||
1690 | $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE; |
||
1691 | // Debug-friendly |
||
1692 | $GLOBALS['wgShowExceptionDetails'] = true; |
||
1693 | // Don't break forms |
||
1694 | $GLOBALS['wgExternalLinkTarget'] = '_blank'; |
||
1695 | |||
1696 | // Extended debugging |
||
1697 | $GLOBALS['wgShowSQLErrors'] = true; |
||
1698 | $GLOBALS['wgShowDBErrorBacktrace'] = true; |
||
1699 | |||
1700 | // Allow multiple ob_flush() calls |
||
1701 | $GLOBALS['wgDisableOutputCompression'] = true; |
||
1702 | |||
1703 | // Use a sensible cookie prefix (not my_wiki) |
||
1704 | $GLOBALS['wgCookiePrefix'] = 'mw_installer'; |
||
1705 | |||
1706 | // Some of the environment checks make shell requests, remove limits |
||
1707 | $GLOBALS['wgMaxShellMemory'] = 0; |
||
1708 | |||
1709 | // Override the default CookieSessionProvider with a dummy |
||
1710 | // implementation that won't stomp on PHP's cookies. |
||
1711 | $GLOBALS['wgSessionProviders'] = [ |
||
1712 | [ |
||
1713 | 'class' => 'InstallerSessionProvider', |
||
1714 | 'args' => [ [ |
||
1715 | 'priority' => 1, |
||
1716 | ] ] |
||
1717 | ] |
||
1718 | ]; |
||
1719 | |||
1720 | // Don't try to use any object cache for SessionManager either. |
||
1721 | $GLOBALS['wgSessionCacheType'] = CACHE_NONE; |
||
1722 | } |
||
1723 | |||
1724 | /** |
||
1725 | * Add an installation step following the given step. |
||
1726 | * |
||
1727 | * @param callable $callback A valid installation callback array, in this form: |
||
1728 | * [ 'name' => 'some-unique-name', 'callback' => [ $obj, 'function' ] ]; |
||
1729 | * @param string $findStep The step to find. Omit to put the step at the beginning |
||
1730 | */ |
||
1731 | public function addInstallStep( $callback, $findStep = 'BEGINNING' ) { |
||
1732 | $this->extraInstallSteps[$findStep][] = $callback; |
||
1733 | } |
||
1734 | |||
1735 | /** |
||
1736 | * Disable the time limit for execution. |
||
1737 | * Some long-running pages (Install, Upgrade) will want to do this |
||
1738 | */ |
||
1739 | protected function disableTimeLimit() { |
||
1740 | MediaWiki\suppressWarnings(); |
||
1741 | set_time_limit( 0 ); |
||
1742 | MediaWiki\restoreWarnings(); |
||
1743 | } |
||
1744 | } |
||
1745 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: