Improvements to handling of 'catastrophic' errors, like unsupported PHP versions...
authorHappy-melon <happy-melon@users.mediawiki.org>
Tue, 12 Apr 2011 20:38:16 +0000 (20:38 +0000)
committerHappy-melon <happy-melon@users.mediawiki.org>
Tue, 12 Apr 2011 20:38:16 +0000 (20:38 +0000)
* 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
includes/Exception.php
includes/GlobalFunctions.php
includes/WebStart.php
includes/db/Database.php
includes/templates/NoLocalSettings.php [deleted file]
includes/templates/PHP4.php [deleted file]
index.php
load.php
php5.php5 [deleted file]

diff --git a/api.php b/api.php
index 6ac1451..f55f853 100644 (file)
--- a/api.php
+++ b/api.php
 // 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 '<pre><b>$wgEnableAPI=true;</b></pre>';
-       die( 1 );
+       wfDie( 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php'
+               . '<pre><b>$wgEnableAPI=true;</b></pre>'
+       );
 }
 
 // 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();
 
index afa46ce..3151cd8 100644 (file)
@@ -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( <<<ENDL
+       body {
+               color: #000;
+               background-color: #fff;
+               font-family: sans-serif;
+               padding: 2em;
+               text-align: center;
+       }
+       p, img, h1 {
+               text-align: left;
+               margin: 0.5em 0;
+       }
+       h1 {
+               font-size: 120%;
+       }
+ENDL
+               );
 
-               $left = 'left';
-               $right = 'right';
                $dir = 'ltr';
                $code = 'en';
 
                if ( $wgLang instanceof Language ) {
-                       $left = $wgLang->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";
                }
        }
 }
index db60f3e..3053553 100644 (file)
@@ -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 );
+       }
 }
 
 /**
index 4a40343..bb2f276 100644 (file)
@@ -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 <a href=\"$path/mw-config/index.$ext\"> complete the installation</a> and download LocalSettings.php."
+                       : "Please <a href=\"$path/mw-config/index.$ext\"> set up the wiki</a> first.";
+
+               wfDie( "<p>LocalSettings.php not found.</p><p>$please</p>" );
        }
 
        # Include site settings. $IP may be changed (hopefully before the AutoLoader is invoked)
index 1fc78ec..1ce91ba 100644 (file)
@@ -2955,7 +2955,7 @@ class DBConnectionError extends DBError {
 
                $this->error = Html::element( 'span', array( 'dir' => 'ltr' ), $this->error );
 
-               $noconnect = "<p><strong>$sorry</strong><br />$again</p><p><small>$info</small></p>";
+               $noconnect = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
                $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 (file)
index 9001e3b..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-/**
- * Template used when there is no LocalSettings.php file
- *
- * @file
- * @ingroup Templates
- */
-
-if ( !isset( $wgVersion ) ) {
-       $wgVersion = 'VERSION';
-}
-$script = $_SERVER['SCRIPT_NAME'];
-$path = pathinfo( $script, PATHINFO_DIRNAME ) . '/';
-$path = str_replace( '//', '/', $path );
-$ext = 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'] ) );
-}
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns='http://www.w3.org/1999/xhtml' lang='en'>
-       <head>
-               <title>MediaWiki <?php echo htmlspecialchars( $wgVersion ) ?></title>
-               <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
-               <style type='text/css' media='screen'>
-                       html, body {
-                               color: #000;
-                               background-color: #fff;
-                               font-family: sans-serif;
-                               text-align: center;
-                       }
-
-                       h1 {
-                               font-size: 150%;
-                       }
-               </style>
-       </head>
-       <body>
-               <img src="<?php echo htmlspecialchars( $path ) ?>skins/common/images/mediawiki.png" alt='The MediaWiki logo' />
-
-               <h1>MediaWiki <?php echo htmlspecialchars( $wgVersion ) ?></h1>
-               <div class='error'>
-               <p>LocalSettings.php not found.</p>
-               <p>
-               <?php
-               if ( $installerStarted ) {
-                       echo( "Please <a href=\"" . htmlspecialchars( $path ) . "mw-config/index." . htmlspecialchars( $ext ) . "\"> complete the installation</a> and download LocalSettings.php." );
-               } else {
-                       echo( "Please <a href=\"" . htmlspecialchars( $path ) . "mw-config/index." . htmlspecialchars( $ext ) . "\"> set up the wiki</a> first." );
-               }
-               ?>
-               </p>
-
-               </div>
-       </body>
-</html>
diff --git a/includes/templates/PHP4.php b/includes/templates/PHP4.php
deleted file mode 100644 (file)
index 6f48238..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-/**
- * Template used when the installer detects that this is PHP 4
- *
- * @file
- * @ingroup Templates
- */
-
-if( !defined( 'MW_PHP4' ) ) {
-       die( "Not an entry point.");
-}
-
-if( isset( $_SERVER['SCRIPT_NAME'] ) ) {
-       // Probably IIS; doesn't set REQUEST_URI
-       $scriptUrl = $_SERVER['SCRIPT_NAME'];
-} elseif( isset( $_SERVER['REQUEST_URI'] ) ) {
-       // We're trying SCRIPT_NAME first because it won't include PATH_INFO... hopefully
-       $scriptUrl = $_SERVER['REQUEST_URI'];
-} else {
-       $scriptUrl = '';
-}
-if ( preg_match( '!^(.*)/(mw-)?config/[^/]*.php$!', $scriptUrl, $m ) ) {
-       $baseUrl = $m[1];
-} elseif ( preg_match( '!^(.*)/[^/]*.php$!', $scriptUrl, $m ) ) {
-       $baseUrl = $m[1];
-} else {
-       $baseUrl = dirname( $scriptUrl );
-}
-
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns='http://www.w3.org/1999/xhtml' lang='en'>
-       <head>
-               <title>MediaWiki <?php echo htmlspecialchars( $wgVersion ); ?></title>
-               <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
-               <style type='text/css' media='screen'>
-                       html, body {
-                               color: #000;
-                               background-color: #fff;
-                               font-family: sans-serif;
-                               text-align: center;
-                       }
-
-                       p {
-                               text-align: left;
-                               margin-left: 2em;
-                               margin-right: 2em;
-                       }
-
-                       h1 {
-                               font-size: 150%;
-                       }
-               </style>
-       </head>
-       <body>
-               <img src="<?php echo htmlspecialchars( $baseUrl ) ?>/skins/common/images/mediawiki.png" alt='The MediaWiki logo' />
-
-               <h1>MediaWiki <?php echo htmlspecialchars( $wgVersion ); ?></h1>
-               <div class='error'>
-<p>
-                       MediaWiki requires PHP 5.2.3 or higher. You are running PHP
-                       <?php echo htmlspecialchars( phpversion() ); ?>.
-</p>
-<?php
-flush();
-/**
- * Test the *.php5 extension
- */
-$downloadOther = true;
-if ( $baseUrl ) {
-       $testUrl = "$wgServer$baseUrl/php5.php5";
-       if( function_exists( 'file_get_contents' ) ) {
-               $errorLevel = error_reporting();
-               error_reporting( $errorLevel & !E_WARNING );
-               
-               ini_set( 'allow_url_fopen', '1' );
-               $s = file_get_contents( $testUrl );
-               
-               error_reporting( $errorLevel );
-       }
-
-       if ( strpos( $s, 'yes' ) !== false ) {
-               $encUrl = htmlspecialchars( str_replace( '.php', '.php5', $scriptUrl ) );
-               echo "<p>You may be able to use MediaWiki using a <a href=\"$encUrl\">.php5</a> file extension.</p>";
-               $downloadOther = false;
-       }
-}
-if ( $downloadOther ) {
-?>
-<p>Please consider
-<a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
-PHP 4 is at the end of its lifecycle and will not receive further security updates.</p>
-<p>If for some reason you really really need to run MediaWiki on PHP 4, you will need to
-<a href="http://www.mediawiki.org/wiki/Download">download version 1.6.x</a>
-from our website. </p>
-<?php
-}
-?>
-
-               </div>
-       </body>
-</html>
index 8faead5..32ab384 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * This is the main web entry point for MediaWiki.
  *
  * @file
  */
 
+// Bail on old versions of PHP.  Pretty much every other file in the codebase
+// has structures (try/catch, foo()->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 = <<<ENDL
+               <p>
+                       MediaWiki requires PHP 5.2.3 or higher. You are running PHP $phpversion.
+               </p>
+               <p>
+                       Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
+                       PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive
+                       security or bugfix updates.
+               </p>
+               <p>
+                       If for some reason you are unable to upgrade your PHP version, you will need to
+                       <a href="http://www.mediawiki.org/wiki/Download">download</a> an older version
+                       of MediaWiki from our website.  See our
+                       <a href="http://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
+                       for details of which versions are compatible with prior versions of PHP.
+               </p>
+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 );
+
+       ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns='http://www.w3.org/1999/xhtml' lang='en'>
+       <head>
+               <title>MediaWiki <?php echo $version; ?></title>
+               <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
+               <style type='text/css' media='screen'>
+                       body {
+                               color: #000;
+                               background-color: #fff;
+                               font-family: sans-serif;
+                               padding: 2em;
+                               text-align: center;
+                       }
+                       p, img, h1 {
+                               text-align: left;
+                               margin: 0.5em 0;
+                       }
+                       h1 {
+                               font-size: 120%;
+                       }
+               </style>
+       </head>
+       <body>
+               <img src="<?php echo $logo; ?>" alt='The MediaWiki logo' />
+               <h1>MediaWiki <?php echo $version; ?> internal error</h1>
+               <div class='error'> <?php echo $errorMsg; ?> </div>
+       </body>
+</html>
+       <?php
+       die( 1 );
+}
\ No newline at end of file
index 89aec98..7fff7c4 100644 (file)
--- a/load.php
+++ b/load.php
  *
  */
 
+// We want error messages to not be interpreted as CSS or JS
+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." );
+}
+
 require ( dirname( __FILE__ ) . '/includes/WebStart.php' );
 wfProfileIn( 'load.php' );
 
@@ -48,5 +61,7 @@ $resourceLoader->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 (file)
index 51e077f..0000000
--- a/php5.php5
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-/** 
- * Test for *.php5 capability in webserver
- * Used by includes/templates/PHP4.php
- */
-if ( version_compare( phpversion(), '5.1.0' ) >= 0 ) {
-       echo 'y'.'e'.'s';
-}