From 938a8eb347d1f14a726729390adbaa3e65ba1bfa Mon Sep 17 00:00:00 2001 From: Happy-melon Date: Tue, 12 Apr 2011 20:38:16 +0000 Subject: [PATCH] Improvements to handling of 'catastrophic' errors, like unsupported PHP versions, no MySQL functions, no LocalSettings, etc. * Fix parsing of the three major entry points (index.php, api.php, load.php) back to PHP 4.4.9. We don't care what happens if you actually try to run these files on old versions, but the entry files need to parse correctly. * consign /includes/templates/PHP4.php and /includes/templates/NoLocalSettings.php to the fiery pit of hell where they belong. * Prevent loading of any other files for PHP < 5. WebStart.php was rendered unparseable in PHP 4 by the introduction of try/catch blocks in r85327. * Die outright with a pretty error message on PHP < 5.2.3 as well as PHP 4. All versions of PHP below that throw parse errors of various sorts. * Reimplement wfDie() to provide an entry-point-dependent die-with-readable-error-message function (for instance, we want a pretty human-readable page in index.php, something wrapped in CSS/JS /*...*/ comment block in load.php, etc). * Standardise the appearance of the catastrophic errors thrown at the top of the stack with the ones lower down (exception-within-exception, etc). There isn't really a way to do this without duplication, AFAICT. --- api.php | 30 ++++++-- includes/Exception.php | 33 +++++--- includes/GlobalFunctions.php | 17 +++-- includes/WebStart.php | 34 +++++---- includes/db/Database.php | 2 +- includes/templates/NoLocalSettings.php | 64 ---------------- includes/templates/PHP4.php | 102 ------------------------- index.php | 84 +++++++++++++++++++- load.php | 19 ++++- php5.php5 | 9 --- 10 files changed, 179 insertions(+), 215 deletions(-) delete mode 100644 includes/templates/NoLocalSettings.php delete mode 100644 includes/templates/PHP4.php delete mode 100644 php5.php5 diff --git a/api.php b/api.php index 6ac1451487..f55f85361a 100644 --- a/api.php +++ b/api.php @@ -37,7 +37,20 @@ // So extensions (and other code) can check whether they're running in API mode define( 'MW_API', true ); -// Initialise common code +// We want a plain message on catastrophic errors that machines can identify +function wfDie( $msg = '' ) { + header( $_SERVER['SERVER_PROTOCOL'] . ' 500 MediaWiki configuration Error', true, 500 ); + echo $msg; + die( 1 ); +} + +// Die on unsupported PHP versions +if( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.2.3' ) < 0 ){ + $version = htmlspecialchars( $wgVersion ); + wfDie( "MediaWiki $version requires at least PHP version 5.2.3." ); +} + +// Initialise common code. require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); wfProfileIn( 'api.php' ); @@ -61,9 +74,9 @@ if ( $wgRequest->isPathInfoBad() ) { // Verify that the API has not been disabled if ( !$wgEnableAPI ) { - echo 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php'; - echo '
$wgEnableAPI=true;
'; - die( 1 ); + wfDie( 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php' + . '
$wgEnableAPI=true;
' + ); } // Selectively allow cross-site AJAX @@ -131,7 +144,8 @@ if ( $wgAPIRequestLog ) { $_SERVER['HTTP_USER_AGENT'] ); $items[] = $wgRequest->wasPosted() ? 'POST' : 'GET'; - if ( $processor->getModule()->mustBePosted() ) { + $module = $processor->getModule(); + if ( $module->mustBePosted() ) { $items[] = "action=" . $wgRequest->getVal( 'action' ); } else { $items[] = wfArrayToCGI( $wgRequest->getValues() ); @@ -140,6 +154,8 @@ if ( $wgAPIRequestLog ) { wfDebug( "Logged API request to $wgAPIRequestLog\n" ); } -// Shut down the database -wfGetLBFactory()->shutdown(); +// Shut down the database. foo()->bar() syntax is not supported in PHP4: we won't ever actually +// get here to worry about whether this should be = or =&, but the file has to parse properly. +$lb = wfGetLBFactory(); +$lb->shutdown(); diff --git a/includes/Exception.php b/includes/Exception.php index afa46ce2ea..3151cd8bdd 100644 --- a/includes/Exception.php +++ b/includes/Exception.php @@ -236,32 +236,44 @@ class MWException extends Exception { header( 'Pragma: nocache' ); } - $title = Html::element( 'title', null, $this->getPageTitle() ); + $head = Html::element( 'title', null, $this->getPageTitle() ) . "\n"; + $head .= Html::inlineStyle( <<alignStart(); - $right = $wgLang->alignEnd(); $dir = $wgLang->getDir(); $code = $wgLang->getCode(); } $header = Html::element( 'img', array( 'src' => $wgLogo, - 'style' => "float: $left; margin-$right: 1em;", 'alt' => '' ), $this->getPageTitle() ); $attribs = array( 'dir' => $dir, 'lang' => $code ); return Html::htmlHeader( $attribs ) . - Html::rawElement( 'head', null, $title ) . "\n". + Html::rawElement( 'head', null, $head ) . "\n". Html::openElement( 'body' ) . "\n" . - Html::rawElement( 'h1', null, $header ) . "\n"; + $header . "\n"; } /** @@ -338,6 +350,7 @@ function wfReportException( Exception $e ) { if ( $e instanceof MWException ) { try { + // Try and show the exception prettily, with the normal skin infrastructure $e->report(); } catch ( Exception $e2 ) { // Exception occurred from within exception handler @@ -359,7 +372,7 @@ function wfReportException( Exception $e ) { if ( $cmdLine ) { wfPrintError( $message ); } else { - echo nl2br( htmlspecialchars( $message ) ) . "\n"; + wfDie( htmlspecialchars( $message ) ) . "\n"; } } } else { @@ -373,7 +386,7 @@ function wfReportException( Exception $e ) { if ( $cmdLine ) { wfPrintError( $message ); } else { - echo nl2br( htmlspecialchars( $message ) ) . "\n"; + wfDie( htmlspecialchars( $message ) ) . "\n"; } } } diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index db60f3e1a4..3053553074 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -851,13 +851,20 @@ function wfErrorExit() { } /** - * Print a simple message and die, returning nonzero to the shell if any. - * Plain die() fails to return nonzero to the shell if you pass a string. + * Print an error message and die, returning nonzero to the shell if any. Plain die() + * fails to return nonzero to the shell if you pass a string. Entry points may customise + * this function to return a prettier error message, but implementations must not assume + * access to any of the usual MediaWiki infrastructure (AutoLoader, localisation, database, + * etc). This should not be called directly once $wgFullyInitialised is set; instead, + * throw an exception and let Exception.php handle whether or not it's possible to show + * a prettier error. * @param $msg String */ -function wfDie( $msg = '' ) { - echo $msg; - die( 1 ); +if( !function_exists( 'wfDie' ) ){ + function wfDie( $msg = '' ) { + echo $msg; + die( 1 ); + } } /** diff --git a/includes/WebStart.php b/includes/WebStart.php index 4a40343e31..bb2f2766cc 100644 --- a/includes/WebStart.php +++ b/includes/WebStart.php @@ -100,16 +100,6 @@ if ( !defined( 'MW_COMPILED' ) ) { # Load up some global defines. require_once( "$IP/includes/Defines.php" ); - # Check for PHP 5 - if ( !function_exists( 'version_compare' ) - || version_compare( phpversion(), '5.0.0' ) < 0 - ) { - define( 'MW_PHP4', '1' ); - require( "$IP/includes/DefaultSettings.php" ); - require( "$IP/includes/templates/PHP4.php" ); - exit; - } - # Start the autoloader, so that extensions can derive classes from core files require_once( "$IP/includes/AutoLoader.php" ); } @@ -126,13 +116,31 @@ if ( defined( 'MW_CONFIG_CALLBACK' ) ) { if ( !defined( 'MW_CONFIG_FILE' ) ) { define('MW_CONFIG_FILE', MWInit::interpretedPath( 'LocalSettings.php' ) ); } - + # LocalSettings.php is the per site customization file. If it does not exist # the wiki installer needs to be launched or the generated file uploaded to # the root wiki directory if( !file_exists( MW_CONFIG_FILE ) ) { - require_once( "$IP/includes/templates/NoLocalSettings.php" ); - die(); + $script = $_SERVER['SCRIPT_NAME']; + $path = htmlspecialchars( str_replace( '//', '/', pathinfo( $script, PATHINFO_DIRNAME ) ) ); + $ext = htmlspecialchars( pathinfo( $script, PATHINFO_EXTENSION ) ); + + # Check to see if the installer is running + if ( !function_exists( 'session_name' ) ) { + $installerStarted = false; + } else { + session_name( 'mw_installer_session' ); + $oldReporting = error_reporting( E_ALL & ~E_NOTICE ); + $success = session_start(); + error_reporting( $oldReporting ); + $installerStarted = ( $success && isset( $_SESSION['installData'] ) ); + } + + $please = $installerStarted + ? "Please complete the installation and download LocalSettings.php." + : "Please set up the wiki first."; + + wfDie( "

LocalSettings.php not found.

$please

" ); } # Include site settings. $IP may be changed (hopefully before the AutoLoader is invoked) diff --git a/includes/db/Database.php b/includes/db/Database.php index 1fc78ec379..1ce91ba44d 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -2955,7 +2955,7 @@ class DBConnectionError extends DBError { $this->error = Html::element( 'span', array( 'dir' => 'ltr' ), $this->error ); - $noconnect = "

$sorry
$again

$info

"; + $noconnect = "

$sorry

$again

$info

"; $text = str_replace( '$1', $this->error, $noconnect ); if ( $wgShowDBErrorBacktrace ) { diff --git a/includes/templates/NoLocalSettings.php b/includes/templates/NoLocalSettings.php deleted file mode 100644 index 9001e3ba8e..0000000000 --- a/includes/templates/NoLocalSettings.php +++ /dev/null @@ -1,64 +0,0 @@ - - - - - MediaWiki <?php echo htmlspecialchars( $wgVersion ) ?> - - - - - The MediaWiki logo - -

MediaWiki

-
-

LocalSettings.php not found.

-

- complete the installation and download LocalSettings.php." ); - } else { - echo( "Please set up the wiki first." ); - } - ?> -

- -
- - diff --git a/includes/templates/PHP4.php b/includes/templates/PHP4.php deleted file mode 100644 index 6f48238106..0000000000 --- a/includes/templates/PHP4.php +++ /dev/null @@ -1,102 +0,0 @@ - - - - - MediaWiki <?php echo htmlspecialchars( $wgVersion ); ?> - - - - - The MediaWiki logo - -

MediaWiki

-
-

- MediaWiki requires PHP 5.2.3 or higher. You are running PHP - . -

-You may be able to use MediaWiki using a .php5 file extension.

"; - $downloadOther = false; - } -} -if ( $downloadOther ) { -?> -

Please consider -upgrading your copy of PHP. -PHP 4 is at the end of its lifecycle and will not receive further security updates.

-

If for some reason you really really need to run MediaWiki on PHP 4, you will need to -download version 1.6.x -from our website.

- - -
- - diff --git a/index.php b/index.php index 8faead547c..32ab38472a 100644 --- a/index.php +++ b/index.php @@ -1,5 +1,4 @@ bar(), etc etc) which throw parse errors in PHP 4. +// Setup.php and ObjectCache.php have structures invalid in PHP 5.0 and 5.1, respectively. +if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.2.0' ) < 0 ) { + $phpversion = htmlspecialchars( phpversion() ); + $errorMsg = << + MediaWiki requires PHP 5.2.3 or higher. You are running PHP $phpversion. +

+

+ Please consider upgrading your copy of PHP. + PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive + security or bugfix updates. +

+

+ If for some reason you are unable to upgrade your PHP version, you will need to + download an older version + of MediaWiki from our website. See our + compatibility page + for details of which versions are compatible with prior versions of PHP. +

+ENDL; + wfDie( $errorMsg ); +} + # Initialise common code. This gives us access to GlobalFunctions, the AutoLoader, and # the globals $wgRequest, $wgOut, $wgUser, $wgLang and $wgContLang, amongst others; it # does *not* load $wgTitle or $wgArticle @@ -47,7 +71,8 @@ wfProfileIn( 'index.php-setup' ); $maxLag = $wgRequest->getVal( 'maxlag' ); if ( !is_null( $maxLag ) ) { - list( $host, $lag ) = wfGetLB()->getMaxLag(); + $lb = wfGetLB(); // foo()->bar() is not supported in PHP4 + list( $host, $lag ) = $lb->getMaxLag(); if ( $lag > $maxLag ) { header( 'HTTP/1.1 503 Service Unavailable' ); header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); @@ -123,3 +148,58 @@ $mediaWiki->finalCleanup(); wfProfileOut( 'index.php' ); $mediaWiki->restInPeace(); + +/** + * Display something vaguely comprehensible in the event of a totally unrecoverable error. + * Does not assume access to *anything*; no globals, no autloader, no database, no localisation. + * Safe for PHP4 (and putting this here means that WebStart.php and GlobalSettings.php + * no longer need to be). + * + * Calling this function kills execution immediately. + * + * @param $errorMsg String fully-escaped HTML + */ +function wfDie( $errorMsg ){ + // Use the version set in DefaultSettings if possible, but don't rely on it + global $wgVersion, $wgLogo; + $version = isset( $wgVersion ) && $wgVersion + ? htmlspecialchars( $wgVersion ) + : ''; + $logo = isset( $wgLogo ) && $wgLogo + ? $wgLogo + : 'http://upload.wikimedia.org/wikipedia/commons/1/1c/MediaWiki_logo.png'; + + header( $_SERVER['SERVER_PROTOCOL'] . ' 500 MediaWiki configuration Error', true, 500 ); + + ?> + + + + MediaWiki <?php echo $version; ?> + + + + + The MediaWiki logo +

MediaWiki internal error

+
+ + + respond( new ResourceLoaderContext( $resourceLoader, $wgRequest wfProfileOut( 'load.php' ); wfLogProfilingData(); -// Shut down the database -wfGetLBFactory()->shutdown(); +// Shut down the database. foo()->bar() syntax is not supported in PHP4, and this file +// needs to *parse* in PHP4, although we'll never get down here to worry about = vs =& +$lb = wfGetLBFactory(); +$lb->shutdown(); diff --git a/php5.php5 b/php5.php5 deleted file mode 100644 index 51e077f054..0000000000 --- a/php5.php5 +++ /dev/null @@ -1,9 +0,0 @@ -= 0 ) { - echo 'y'.'e'.'s'; -} -- 2.20.1