installer: Fix grammar error in r70053 pointed out by http://github.com/wlangstroth
[lhc/web/wiklou.git] / includes / installer / Installer.php
1 <?php
2
3 /**
4 * Base installer class.
5 *
6 * This class provides the base for installation and update functionality
7 * for both MediaWiki core and extensions.
8 *
9 * @since 1.17
10 */
11 abstract class Installer {
12
13 /**
14 * TODO: make protected?
15 *
16 * @var array
17 */
18 public $settings;
19
20 /**
21 * Cached DB installer instances, access using getDBInstaller().
22 *
23 * @var array
24 */
25 protected $dbInstallers = array();
26
27 /**
28 * Minimum memory size in MB.
29 *
30 * @var integer
31 */
32 protected $minMemorySize = 50;
33
34 /**
35 * Cached Title, used by parse().
36 *
37 * @var Title
38 */
39 protected $parserTitle;
40
41 /**
42 * Cached ParserOptions, used by parse().
43 *
44 * @var ParserOptions
45 */
46 protected $parserOptions;
47
48 /**
49 * Known database types. These correspond to the class names <type>Installer,
50 * and are also MediaWiki database types valid for $wgDBtype.
51 *
52 * To add a new type, create a <type>Installer class and a Database<type>
53 * class, and add a config-type-<type> message to MessagesEn.php.
54 *
55 * @var array
56 */
57 protected $dbTypes = array(
58 'mysql',
59 'postgres',
60 'sqlite',
61 'oracle'
62 );
63
64 /**
65 * A list of environment check methods called by doEnvironmentChecks().
66 * These may output warnings using showMessage(), and/or abort the
67 * installation process by returning false.
68 *
69 * @var array
70 */
71 protected $envChecks = array(
72 'envLatestVersion',
73 'envCheckDB',
74 'envCheckRegisterGlobals',
75 'envCheckMagicQuotes',
76 'envCheckMagicSybase',
77 'envCheckMbstring',
78 'envCheckZE1',
79 'envCheckSafeMode',
80 'envCheckXML',
81 'envCheckPCRE',
82 'envCheckMemory',
83 'envCheckCache',
84 'envCheckDiff3',
85 'envCheckGraphics',
86 'envCheckPath',
87 'envCheckWriteableDir',
88 'envCheckExtension',
89 'envCheckShellLocale',
90 'envCheckUploadsDirectory',
91 );
92
93 /**
94 * UI interface for displaying a short message
95 * The parameters are like parameters to wfMsg().
96 * The messages will be in wikitext format, which will be converted to an
97 * output format such as HTML or text before being sent to the user.
98 */
99 public abstract function showMessage( $msg /*, ... */ );
100
101 /**
102 * TODO: doucment
103 *
104 * @param $status
105 */
106 public abstract function showStatusMessage( $status );
107
108 /**
109 * Constructor, always call this from child classes.
110 */
111 public function __construct() {
112 // Disable the i18n cache and LoadBalancer
113 Language::getLocalisationCache()->disableBackend();
114 LBFactory::disableBackend();
115 }
116
117 /**
118 * Get a list of known DB types.
119 */
120 public function getDBTypes() {
121 return $this->dbTypes;
122 }
123
124 /**
125 * Do initial checks of the PHP environment. Set variables according to
126 * the observed environment.
127 *
128 * It's possible that this may be called under the CLI SAPI, not the SAPI
129 * that the wiki will primarily run under. In that case, the subclass should
130 * initialise variables such as wgScriptPath, before calling this function.
131 *
132 * Under the web subclass, it can already be assumed that PHP 5+ is in use
133 * and that sessions are working.
134 *
135 * @return boolean
136 */
137 public function doEnvironmentChecks() {
138 $this->showMessage( 'config-env-php', phpversion() );
139
140 $good = true;
141
142 foreach ( $this->envChecks as $check ) {
143 $status = $this->$check();
144 if ( $status === false ) {
145 $good = false;
146 }
147 }
148
149 $this->setVar( '_Environment', $good );
150
151 if ( $good ) {
152 $this->showMessage( 'config-env-good' );
153 } else {
154 $this->showMessage( 'config-env-bad' );
155 }
156
157 return $good;
158 }
159
160 /**
161 * Set a MW configuration variable, or internal installer configuration variable.
162 *
163 * @param $name String
164 * @param $value Mixed
165 */
166 public function setVar( $name, $value ) {
167 $this->settings[$name] = $value;
168 }
169
170 /**
171 * Get an MW configuration variable, or internal installer configuration variable.
172 * The defaults come from $GLOBALS (ultimately DefaultSettings.php).
173 * Installer variables are typically prefixed by an underscore.
174 *
175 * @param $name String
176 * @param $default Mixed
177 *
178 * @return mixed
179 */
180 public function getVar( $name, $default = null ) {
181 if ( !isset( $this->settings[$name] ) ) {
182 return $default;
183 } else {
184 return $this->settings[$name];
185 }
186 }
187
188 /**
189 * Get an instance of DatabaseInstaller for the specified DB type.
190 *
191 * @param $type Mixed: DB installer for which is needed, false to use default.
192 *
193 * @return DatabaseInstaller
194 */
195 public function getDBInstaller( $type = false ) {
196 if ( !$type ) {
197 $type = $this->getVar( 'wgDBtype' );
198 }
199
200 $type = strtolower( $type );
201
202 if ( !isset( $this->dbInstallers[$type] ) ) {
203 $class = ucfirst( $type ). 'Installer';
204 $this->dbInstallers[$type] = new $class( $this );
205 }
206
207 return $this->dbInstallers[$type];
208 }
209
210 /**
211 * Determine if LocalSettings exists. If it does, return an appropriate
212 * status for whether we should can upgrade or not.
213 *
214 * @return Status
215 */
216 public function getLocalSettingsStatus() {
217 global $IP;
218
219 $status = Status::newGood();
220
221 wfSuppressWarnings();
222 $ls = file_exists( "$IP/LocalSettings.php" );
223 wfRestoreWarnings();
224
225 if( $ls ) {
226 if( $this->getDBInstaller()->needsUpgrade() ) {
227 $status->warning( 'config-localsettings-upgrade' );
228 }
229 else {
230 $status->fatal( 'config-localsettings-noupgrade' );
231 }
232 }
233
234 return $status;
235 }
236
237 /**
238 * Get a fake password for sending back to the user in HTML.
239 * This is a security mechanism to avoid compromise of the password in the
240 * event of session ID compromise.
241 *
242 * @param $realPassword String
243 *
244 * @return string
245 */
246 public function getFakePassword( $realPassword ) {
247 return str_repeat( '*', strlen( $realPassword ) );
248 }
249
250 /**
251 * Set a variable which stores a password, except if the new value is a
252 * fake password in which case leave it as it is.
253 *
254 * @param $name String
255 * @param $value Mixed
256 */
257 public function setPassword( $name, $value ) {
258 if ( !preg_match( '/^\*+$/', $value ) ) {
259 $this->setVar( $name, $value );
260 }
261 }
262
263 /**
264 * On POSIX systems return the primary group of the webserver we're running under.
265 * On other systems just returns null.
266 *
267 * This is used to advice the user that he should chgrp his config/data/images directory as the
268 * webserver user before he can install.
269 *
270 * Public because SqliteInstaller needs it, and doesn't subclass Installer.
271 *
272 * @return mixed
273 */
274 public static function maybeGetWebserverPrimaryGroup() {
275 if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
276 # I don't know this, this isn't UNIX.
277 return null;
278 }
279
280 # posix_getegid() *not* getmygid() because we want the group of the webserver,
281 # not whoever owns the current script.
282 $gid = posix_getegid();
283 $getpwuid = posix_getpwuid( $gid );
284 $group = $getpwuid['name'];
285
286 return $group;
287 }
288
289 /**
290 * Convert wikitext $text to HTML.
291 *
292 * This is potentially error prone since many parser features require a complete
293 * installed MW database. The solution is to just not use those features when you
294 * write your messages. This appears to work well enough. Basic formatting and
295 * external links work just fine.
296 *
297 * But in case a translator decides to throw in a #ifexist or internal link or
298 * whatever, this function is guarded to catch attempted DB access and to present
299 * some fallback text.
300 *
301 * @param $text String
302 * @param $lineStart Boolean
303 * @return String
304 */
305 public function parse( $text, $lineStart = false ) {
306 global $wgParser;
307
308 try {
309 $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
310 $html = $out->getText();
311 } catch ( DBAccessError $e ) {
312 $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
313
314 if ( !empty( $this->debug ) ) {
315 $html .= "<!--\n" . $e->getTraceAsString() . "\n-->";
316 }
317 }
318
319 return $html;
320 }
321
322 /**
323 * TODO: document
324 *
325 * @param DatabaseInstaller $installer
326 *
327 * @return Status
328 */
329 public function installDatabase( DatabaseInstaller &$installer ) {
330 if( !$installer ) {
331 $type = $this->getVar( 'wgDBtype' );
332 $status = Status::newFatal( "config-no-db", $type );
333 } else {
334 $status = $installer->setupDatabase();
335 }
336
337 return $status;
338 }
339
340 /**
341 * TODO: document
342 *
343 * @param DatabaseInstaller $installer
344 *
345 * @return Status
346 */
347 public function installTables( DatabaseInstaller &$installer ) {
348 $status = $installer->createTables();
349
350 if( $status->isOK() ) {
351 LBFactory::enableBackend();
352 }
353
354 return $status;
355 }
356
357 /**
358 * TODO: document
359 *
360 * @param DatabaseInstaller $installer
361 *
362 * @return Status
363 */
364 public function installInterwiki( DatabaseInstaller &$installer ) {
365 return $installer->populateInterwikiTable();
366 }
367
368 /**
369 * Exports all wg* variables stored by the installer into global scope.
370 */
371 public function exportVars() {
372 foreach ( $this->settings as $name => $value ) {
373 if ( substr( $name, 0, 2 ) == 'wg' ) {
374 $GLOBALS[$name] = $value;
375 }
376 }
377 }
378
379 /**
380 * Check if we're installing the latest version.
381 */
382 public function envLatestVersion() {
383 global $wgVersion;
384
385 $latestInfoUrl = 'http://www.mediawiki.org/w/api.php?action=mwreleases&format=json';
386 $latestInfo = Http::get( $latestInfoUrl );
387
388 if( !$latestInfo ) {
389 $this->showMessage( 'config-env-latest-can-not-check', $latestInfoUrl );
390 return;
391 }
392
393 $this->setVar( '_ExternalHTTP', true );
394 $latestInfo = FormatJson::decode($latestInfo);
395
396 if ($latestInfo === false || !isset( $latestInfo->mwreleases ) ) {
397 # For when the request is successful but there's e.g. some silly man in
398 # the middle firewall blocking us, e.g. one of those annoying airport ones
399 $this->showMessage( 'config-env-latest-data-invalid', $latestInfoUrl );
400 return;
401 }
402
403 foreach( $latestInfo->mwreleases as $rel ) {
404 if( isset( $rel->current ) ) {
405 $currentVersion = $rel->version;
406 }
407 }
408
409 if( version_compare( $wgVersion, $currentVersion, '<' ) ) {
410 $this->showMessage( 'config-env-latest-old' );
411 $this->showHelpBox( 'config-env-latest-help', $wgVersion, $currentVersion );
412 } elseif( version_compare( $wgVersion, $currentVersion, '>' ) ) {
413 $this->showMessage( 'config-env-latest-new' );
414 }
415
416 $this->showMessage( 'config-env-latest-ok' );
417 }
418
419 /**
420 * Environment check for DB types.
421 */
422 public function envCheckDB() {
423 global $wgLang;
424
425 $compiledDBs = array();
426 $goodNames = array();
427 $allNames = array();
428
429 foreach ( $this->dbTypes as $name ) {
430 $db = $this->getDBInstaller( $name );
431 $readableName = wfMsg( 'config-type-' . $name );
432
433 if ( $db->isCompiled() ) {
434 $compiledDBs[] = $name;
435 $goodNames[] = $readableName;
436 }
437
438 $allNames[] = $readableName;
439 }
440
441 $this->setVar( '_CompiledDBs', $compiledDBs );
442
443 if ( !$compiledDBs ) {
444 $this->showMessage( 'config-no-db' );
445 $this->showHelpBox( 'config-no-db-help', $wgLang->commaList( $allNames ) );
446 return false;
447 }
448
449 $this->showMessage( 'config-have-db', $wgLang->commaList( $goodNames ) );
450 }
451
452 /**
453 * Environment check for register_globals.
454 */
455 public function envCheckRegisterGlobals() {
456 if( wfIniGetBool( "magic_quotes_runtime" ) ) {
457 $this->showMessage( 'config-register-globals' );
458 }
459 }
460
461 /**
462 * Environment check for magic_quotes_runtime.
463 */
464 public function envCheckMagicQuotes() {
465 if( wfIniGetBool( "magic_quotes_runtime" ) ) {
466 $this->showMessage( 'config-magic-quotes-runtime' );
467 return false;
468 }
469 }
470
471 /**
472 * Environment check for magic_quotes_sybase.
473 */
474 public function envCheckMagicSybase() {
475 if ( wfIniGetBool( 'magic_quotes_sybase' ) ) {
476 $this->showMessage( 'config-magic-quotes-sybase' );
477 return false;
478 }
479 }
480
481 /**
482 * Environment check for mbstring.func_overload.
483 */
484 public function envCheckMbstring() {
485 if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
486 $this->showMessage( 'config-mbstring' );
487 return false;
488 }
489 }
490
491 /**
492 * Environment check for zend.ze1_compatibility_mode.
493 */
494 public function envCheckZE1() {
495 if ( wfIniGetBool( 'zend.ze1_compatibility_mode' ) ) {
496 $this->showMessage( 'config-ze1' );
497 return false;
498 }
499 }
500
501 /**
502 * Environment check for safe_mode.
503 */
504 public function envCheckSafeMode() {
505 if ( wfIniGetBool( 'safe_mode' ) ) {
506 $this->setVar( '_SafeMode', true );
507 $this->showMessage( 'config-safe-mode' );
508 }
509 }
510
511 /**
512 * Environment check for the XML module.
513 */
514 public function envCheckXML() {
515 if ( !function_exists( "utf8_encode" ) ) {
516 $this->showMessage( 'config-xml-bad' );
517 return false;
518 }
519 $this->showMessage( 'config-xml-good' );
520 }
521
522 /**
523 * Environment check for the PCRE module.
524 */
525 public function envCheckPCRE() {
526 if ( !function_exists( 'preg_match' ) ) {
527 $this->showMessage( 'config-pcre' );
528 return false;
529 }
530 }
531
532 /**
533 * Environment check for available memory.
534 */
535 public function envCheckMemory() {
536 $limit = ini_get( 'memory_limit' );
537
538 if ( !$limit || $limit == -1 ) {
539 $this->showMessage( 'config-memory-none' );
540 return true;
541 }
542
543 $n = intval( $limit );
544
545 if( preg_match( '/^([0-9]+)[Mm]$/', trim( $limit ), $m ) ) {
546 $n = intval( $m[1] * ( 1024 * 1024 ) );
547 }
548
549 if( $n < $this->minMemorySize * 1024 * 1024 ) {
550 $newLimit = "{$this->minMemorySize}M";
551
552 if( ini_set( "memory_limit", $newLimit ) === false ) {
553 $this->showMessage( 'config-memory-bad', $limit );
554 } else {
555 $this->showMessage( 'config-memory-raised', $limit, $newLimit );
556 $this->setVar( '_RaiseMemory', true );
557 }
558 } else {
559 $this->showMessage( 'config-memory-ok', $limit );
560 }
561 }
562
563 /**
564 * Environment check for compiled object cache types.
565 */
566 public function envCheckCache() {
567 $caches = array();
568
569 foreach ( $this->objectCaches as $name => $function ) {
570 if ( function_exists( $function ) ) {
571 $caches[$name] = true;
572 $this->showMessage( 'config-' . $name );
573 }
574 }
575
576 if ( !$caches ) {
577 $this->showMessage( 'config-no-cache' );
578 }
579
580 $this->setVar( '_Caches', $caches );
581 }
582
583 /**
584 * Search for GNU diff3.
585 */
586 public function envCheckDiff3() {
587 $paths = array_merge(
588 array(
589 "/usr/bin",
590 "/usr/local/bin",
591 "/opt/csw/bin",
592 "/usr/gnu/bin",
593 "/usr/sfw/bin"
594 ),
595 explode( PATH_SEPARATOR, getenv( "PATH" ) )
596 );
597
598 $names = array( "gdiff3", "diff3", "diff3.exe" );
599 $versionInfo = array( '$1 --version 2>&1', 'diff3 (GNU diffutils)' );
600
601 $haveDiff3 = false;
602
603 foreach ( $paths as $path ) {
604 $exe = $this->locateExecutable( $path, $names, $versionInfo );
605
606 if ($exe !== false) {
607 $this->setVar( 'wgDiff3', $exe );
608 $haveDiff3 = true;
609 break;
610 }
611 }
612
613 if ( $haveDiff3 ) {
614 $this->showMessage( 'config-diff3-good', $exe );
615 } else {
616 $this->setVar( 'wgDiff3', false );
617 $this->showMessage( 'config-diff3-bad' );
618 }
619 }
620
621 /**
622 * Environment check for ImageMagick and GD.
623 */
624 public function envCheckGraphics() {
625 $imcheck = array( "/usr/bin", "/opt/csw/bin", "/usr/local/bin", "/sw/bin", "/opt/local/bin" );
626
627 foreach( $imcheck as $dir ) {
628 $im = "$dir/convert";
629
630 wfSuppressWarnings();
631 $file_exists = file_exists( $im );
632 wfRestoreWarnings();
633
634 if( $file_exists ) {
635 $this->showMessage( 'config-imagemagick', $im );
636 $this->setVar( 'wgImageMagickConvertCommand', $im );
637 return true;
638 }
639 }
640
641 if ( function_exists( 'imagejpeg' ) ) {
642 $this->showMessage( 'config-gd' );
643 return true;
644 }
645
646 $this->showMessage( 'no-scaling' );
647 }
648
649 /**
650 * Environment check for setting $IP and $wgScriptPath.
651 */
652 public function envCheckPath() {
653 global $IP;
654 $IP = dirname( dirname( dirname( __FILE__ ) ) );
655
656 $this->setVar( 'IP', $IP );
657 $this->showMessage( 'config-dir', $IP );
658
659 // PHP_SELF isn't available sometimes, such as when PHP is CGI but
660 // cgi.fix_pathinfo is disabled. In that case, fall back to SCRIPT_NAME
661 // to get the path to the current script... hopefully it's reliable. SIGH
662 if ( !empty( $_SERVER['PHP_SELF'] ) ) {
663 $path = $_SERVER['PHP_SELF'];
664 } elseif ( !empty( $_SERVER['SCRIPT_NAME'] ) ) {
665 $path = $_SERVER['SCRIPT_NAME'];
666 } elseif ( $this->getVar( 'wgScriptPath' ) ) {
667 // Some kind soul has set it for us already (e.g. debconf)
668 return true;
669 } else {
670 $this->showMessage( 'config-no-uri' );
671 return false;
672 }
673
674 $uri = preg_replace( '{^(.*)/config.*$}', '$1', $path );
675 $this->setVar( 'wgScriptPath', $uri );
676 $this->showMessage( 'config-uri', $uri );
677 }
678
679 /**
680 * Environment check for writable config/ directory.
681 */
682 public function envCheckWriteableDir() {
683 $ipDir = $this->getVar( 'IP' );
684 $configDir = $ipDir . '/config';
685
686 if( !is_writeable( $configDir ) ) {
687 $webserverGroup = self::maybeGetWebserverPrimaryGroup();
688
689 if ( $webserverGroup !== null ) {
690 $this->showMessage( 'config-dir-not-writable-group', $ipDir, $webserverGroup );
691 } else {
692 $this->showMessage( 'config-dir-not-writable-nogroup', $ipDir, $webserverGroup );
693 }
694
695 return false;
696 }
697 }
698
699 /**
700 * Environment check for setting the preferred PHP file extension.
701 */
702 public function envCheckExtension() {
703 // FIXME: detect this properly
704 if ( defined( 'MW_INSTALL_PHP5_EXT' ) ) {
705 $ext = 'php5';
706 } else {
707 $ext = 'php';
708 }
709
710 $this->setVar( 'wgScriptExtension', ".$ext" );
711 $this->showMessage( 'config-file-extension', $ext );
712 }
713
714 /**
715 * TODO: document
716 */
717 public function envCheckShellLocale() {
718 # Give up now if we're in safe mode or open_basedir.
719 # It's theoretically possible but tricky to work with.
720 if ( wfIniGetBool( "safe_mode" ) || ini_get( 'open_basedir' ) || !function_exists( 'exec' ) ) {
721 return true;
722 }
723
724 $os = php_uname( 's' );
725 $supported = array( 'Linux', 'SunOS', 'HP-UX' ); # Tested these
726
727 if ( !in_array( $os, $supported ) ) {
728 return true;
729 }
730
731 # Get a list of available locales.
732 $lines = $ret = false;
733 exec( '/usr/bin/locale -a', $lines, $ret );
734
735 if ( $ret ) {
736 return true;
737 }
738
739 $lines = wfArrayMap( 'trim', $lines );
740 $candidatesByLocale = array();
741 $candidatesByLang = array();
742
743 foreach ( $lines as $line ) {
744 if ( $line === '' ) {
745 continue;
746 }
747
748 if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) {
749 continue;
750 }
751
752 list( $all, $lang, $territory, $charset, $modifier ) = $m;
753
754 $candidatesByLocale[$m[0]] = $m;
755 $candidatesByLang[$lang][] = $m;
756 }
757
758 # Try the current value of LANG.
759 if ( isset( $candidatesByLocale[ getenv( 'LANG' ) ] ) ) {
760 $this->setVar( 'wgShellLocale', getenv( 'LANG' ) );
761 $this->showMessage( 'config-shell-locale', getenv( 'LANG' ) );
762 return true;
763 }
764
765 # Try the most common ones.
766 $commonLocales = array( 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' );
767 foreach ( $commonLocales as $commonLocale ) {
768 if ( isset( $candidatesByLocale[$commonLocale] ) ) {
769 $this->setVar( 'wgShellLocale', $commonLocale );
770 $this->showMessage( 'config-shell-locale', $commonLocale );
771 return true;
772 }
773 }
774
775 # Is there an available locale in the Wiki's language?
776 $wikiLang = $this->getVar( 'wgLanguageCode' );
777
778 if ( isset( $candidatesByLang[$wikiLang] ) ) {
779 $m = reset( $candidatesByLang[$wikiLang] );
780 $this->setVar( 'wgShellLocale', $m[0] );
781 $this->showMessage( 'config-shell-locale', $m[0] );
782 return true;
783 }
784
785 # Are there any at all?
786 if ( count( $candidatesByLocale ) ) {
787 $m = reset( $candidatesByLocale );
788 $this->setVar( 'wgShellLocale', $m[0] );
789 $this->showMessage( 'config-shell-locale', $m[0] );
790 return true;
791 }
792
793 # Give up.
794 return true;
795 }
796
797 /**
798 * TODO: document
799 */
800 public function envCheckUploadsDirectory() {
801 global $IP, $wgServer;
802
803 $dir = $IP . '/images/';
804 $url = $wgServer . $this->getVar( 'wgScriptPath' ) . '/images/';
805 $safe = !$this->dirIsExecutable( $dir, $url );
806
807 if ( $safe ) {
808 $this->showMessage( 'config-uploads-safe' );
809 } else {
810 $this->showMessage( 'config-uploads-not-safe', $dir );
811 }
812 }
813
814 /**
815 * Search a path for any of the given executable names. Returns the
816 * executable name if found. Also checks the version string returned
817 * by each executable.
818 *
819 * Used only by environment checks.
820 *
821 * @param $path String: path to search
822 * @param $names Array of executable names
823 * @param $versionInfo Boolean false or array with two members:
824 * 0 => Command to run for version check, with $1 for the path
825 * 1 => String to compare the output with
826 *
827 * If $versionInfo is not false, only executables with a version
828 * matching $versionInfo[1] will be returned.
829 */
830 protected function locateExecutable( $path, $names, $versionInfo = false ) {
831 if ( !is_array( $names ) ) {
832 $names = array( $names );
833 }
834
835 foreach ( $names as $name ) {
836 $command = "$path/$name";
837
838 wfSuppressWarnings();
839 $file_exists = file_exists( $command );
840 wfRestoreWarnings();
841
842 if ( $file_exists ) {
843 if ( !$versionInfo ) {
844 return $command;
845 }
846
847 $file = str_replace( '$1', $command, $versionInfo[0] );
848
849 # Should maybe be wfShellExec( $file), but runs into a ulimit, see
850 # http://www.mediawiki.org/w/index.php?title=New-installer_issues&diff=prev&oldid=335456
851 if ( strstr( `$file`, $versionInfo[1]) !== false ) {
852 return $command;
853 }
854 }
855 }
856
857 return false;
858 }
859
860 /**
861 * Checks if scripts located in the given directory can be executed via the given URL.
862 *
863 * Used only by environment checks.
864 */
865 public function dirIsExecutable( $dir, $url ) {
866 $scriptTypes = array(
867 'php' => array(
868 "<?php echo 'ex' . 'ec';",
869 "#!/var/env php5\n<?php echo 'ex' . 'ec';",
870 ),
871 );
872
873 // it would be good to check other popular languages here, but it'll be slow.
874
875 wfSuppressWarnings();
876
877 foreach ( $scriptTypes as $ext => $contents ) {
878 foreach ( $contents as $source ) {
879 $file = 'exectest.' . $ext;
880
881 if ( !file_put_contents( $dir . $file, $source ) ) {
882 break;
883 }
884
885 $text = Http::get( $url . $file );
886 unlink( $dir . $file );
887
888 if ( $text == 'exec' ) {
889 wfRestoreWarnings();
890 return $ext;
891 }
892 }
893 }
894
895 wfRestoreWarnings();
896
897 return false;
898 }
899
900 }