Some initial work on exceptions. More to follow.
authorTim Starling <tstarling@users.mediawiki.org>
Tue, 6 Jun 2006 23:07:26 +0000 (23:07 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Tue, 6 Jun 2006 23:07:26 +0000 (23:07 +0000)
includes/Database.php
includes/DatabaseOracle.php
includes/DatabasePostgreSQL.php
includes/Exception.php [new file with mode: 0644]
includes/LoadBalancer.php
includes/Setup.php

index 2bdf610..173bc71 100644 (file)
@@ -24,8 +24,12 @@ define( 'DEADLOCK_DELAY_MIN', 500000 );
 /** Maximum time to wait before retry */
 define( 'DEADLOCK_DELAY_MAX', 1500000 );
 
+/******************************************************************************
+ * Utility classes
+ *****************************************************************************/
+
 class DBObject {
-       var $mData;
+       public $mData;
 
        function DBObject($data) {
                $this->mData = $data;
@@ -40,6 +44,190 @@ class DBObject {
        }
 };
 
+/******************************************************************************
+ * Error classes
+ *****************************************************************************/
+
+/**
+ * Database error base class
+ */
+class DBError extends MWException {
+       public $db;
+
+       /**
+        * Construct a database error
+        * @param Database $db The database object which threw the error
+        * @param string $error A simple error message to be used for debugging
+        */
+       function __construct( Database &$db, $error ) {
+               $this->db =& $db;
+               parent::__construct( $error );
+       }
+}
+
+class DBConnectionError extends DBError {
+       public $error;
+       
+       function __construct( Database &$db, $error = 'unknown error' ) {
+               $msg = 'DB connection error';
+               if ( trim( $error ) != '' ) {
+                       $msg .= ": $error";
+               }
+               $this->error = $error;
+               parent::__construct( $db, $msg );
+       }
+
+       function useOutputPage() {
+               // Not likely to work
+               return false;
+       }
+
+       function useMessageCache() {
+               // Not likely to work
+               return false;
+       }
+       
+       function getText() {
+               return $this->getMessage() . "\n";
+       }
+
+       function getPageTitle() {
+               global $wgSitename;
+               return "$wgSitename has a problem";
+       }
+
+       function getHTML() {
+               global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgOutputEncoding;
+               global $wgSitename, $wgServer, $wgMessageCache, $wgLogo;
+
+               # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky.
+               # Hard coding strings instead.
+
+               $noconnect = "<p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $1)</small></p>";
+               $mainpage = 'Main Page';
+               $searchdisabled = <<<EOT
+<p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime.
+<span style="font-size: 89%; display: block; margin-left: .2em">Note that their indexes of $wgSitename content may be out of date.</span></p>',
+EOT;
+
+               $googlesearch = "
+<!-- SiteSearch Google -->
+<FORM method=GET action=\"http://www.google.com/search\">
+<TABLE bgcolor=\"#FFFFFF\"><tr><td>
+<A HREF=\"http://www.google.com/\">
+<IMG SRC=\"http://www.google.com/logos/Logo_40wht.gif\"
+border=\"0\" ALT=\"Google\"></A>
+</td>
+<td>
+<INPUT TYPE=text name=q size=31 maxlength=255 value=\"$1\">
+<INPUT type=submit name=btnG VALUE=\"Google Search\">
+<font size=-1>
+<input type=hidden name=domains value=\"$wgServer\"><br /><input type=radio name=sitesearch value=\"\"> WWW <input type=radio name=sitesearch value=\"$wgServer\" checked> $wgServer <br />
+<input type='hidden' name='ie' value='$2'>
+<input type='hidden' name='oe' value='$2'>
+</font>
+</td></tr></TABLE>
+</FORM>
+<!-- SiteSearch Google -->";
+               $cachederror = "The following is a cached copy of the requested page, and may not be up to date. ";
+
+               # No database access
+               if ( is_object( $wgMessageCache ) ) {
+                       $wgMessageCache->disable();
+               }
+
+               if ( trim( $this->error ) == '' ) {
+                       $this->error = $this->db->getProperty('mServer');
+               }
+
+               $text = str_replace( '$1', $this->error, $noconnect );
+               $text .= wfGetSiteNotice();
+
+               if($wgUseFileCache) {
+                       if($wgTitle) {
+                               $t =& $wgTitle;
+                       } else {
+                               if($title) {
+                                       $t = Title::newFromURL( $title );
+                               } elseif (@/**/$_REQUEST['search']) {
+                                       $search = $_REQUEST['search'];
+                                       return $searchdisabled .
+                                         str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ),
+                                         $wgInputEncoding ), $googlesearch );
+                               } else {
+                                       $t = Title::newFromText( $mainpage );
+                               }
+                       }
+
+                       $cache = new CacheManager( $t );
+                       if( $cache->isFileCached() ) {
+                               $msg = '<p style="color: red"><b>'.$msg."<br />\n" .
+                                       $cachederror . "</b></p>\n";
+
+                               $tag = '<div id="article">';
+                               $text = str_replace(
+                                       $tag,
+                                       $tag . $msg,
+                                       $cache->fetchPageText() );
+                       }
+               }
+
+               return $text;
+       }
+}
+
+class DBQueryError extends DBError {
+       public $error, $errno, $sql, $fname;
+       
+       function __construct( Database &$db, $error, $errno, $sql, $fname ) {
+               $message = "A database error has occurred\n" .
+                 "Query: $sql\n" .
+                 "Function: $fname\n" .
+                 "Error: $errno $error\n";
+
+               parent::__construct( $db, $message );
+               $this->error = $error;
+               $this->errno = $errno;
+               $this->sql = $sql;
+               $this->fname = $fname;
+       }
+
+       function getText() {
+               if ( $this->useMessageCache() ) {
+                       return wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ),
+                         htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n";
+               } else {
+                       return $this->getMessage();
+               }
+       }
+       
+       function getSQL() {
+               global $wgShowSQLErrors;
+               if( !$wgShowSQLErrors ) {
+                       return $this->msg( 'sqlhidden', 'SQL hidden' );
+               } else {
+                       return $this->sql;
+               }
+       }
+       
+       function getPageTitle() {
+               return $this->msg( 'databaseerror', 'Database error' );
+       }
+
+       function getHTML() {
+               if ( $this->useMessageCache() ) {
+                       return wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ),
+                         htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) );
+               } else {
+                       return nl2br( htmlspecialchars( $this->getMessage() ) );
+               }
+       }
+}
+
+class DBUnexpectedError extends DBError {}
+
+/******************************************************************************/
+
 /**
  * Database abstraction object
  * @package MediaWiki
@@ -49,21 +237,18 @@ class Database {
 #------------------------------------------------------------------------------
 # Variables
 #------------------------------------------------------------------------------
-       /**#@+
-        * @private
-        */
-       var $mLastQuery = '';
 
-       var $mServer, $mUser, $mPassword, $mConn = null, $mDBname;
-       var $mOut, $mOpened = false;
+       protected $mLastQuery = '';
 
-       var $mFailFunction;
-       var $mTablePrefix;
-       var $mFlags;
-       var $mTrxLevel = 0;
-       var $mErrorCount = 0;
-       var $mLBInfo = array();
-       /**#@-*/
+       protected $mServer, $mUser, $mPassword, $mConn = null, $mDBname;
+       protected $mOut, $mOpened = false;
+
+       protected $mFailFunction;
+       protected $mTablePrefix;
+       protected $mFlags;
+       protected $mTrxLevel = 0;
+       protected $mErrorCount = 0;
+       protected $mLBInfo = array();
 
 #------------------------------------------------------------------------------
 # Accessors
@@ -173,6 +358,13 @@ class Database {
                return !!($this->mFlags & $flag);
        }
 
+       /**
+        * General read-only accessor
+        */
+       function getProperty( $name ) {
+               return $this->$name;
+       }
+
 #------------------------------------------------------------------------------
 # Other functions
 #------------------------------------------------------------------------------
@@ -189,7 +381,7 @@ class Database {
         * @param $flags
         * @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php
         */
-       function Database( $server = false, $user = false, $password = false, $dbName = false,
+       function __construct( $server = false, $user = false, $password = false, $dbName = false,
                $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) {
 
                global $wgOut, $wgDBprefix, $wgCommandLineMode;
@@ -234,7 +426,7 @@ class Database {
         * @param failFunction
         * @param $flags
         */
-       function newFromParams( $server, $user, $password, $dbName,
+       static function newFromParams( $server, $user, $password, $dbName,
                $failFunction = false, $flags = 0 )
        {
                return new Database( $server, $user, $password, $dbName, $failFunction, $flags );
@@ -253,9 +445,10 @@ class Database {
                        @dl('mysql.so');
                }
 
+               # Fail now
                # Otherwise we get a suppressed fatal error, which is very hard to track down
                if ( !function_exists( 'mysql_connect' ) ) {
-                       wfDie( "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
+                       throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
                }
 
                $this->close();
@@ -328,7 +521,6 @@ class Database {
        }
 
        /**
-        * @private
         * @param string $error fallback error message, used if none is given by MySQL
         */
        function reportConnectionError( $error = 'Unknown error' ) {
@@ -338,12 +530,15 @@ class Database {
                }
 
                if ( $this->mFailFunction ) {
+                       # Legacy error handling method
                        if ( !is_int( $this->mFailFunction ) ) {
                                $ff = $this->mFailFunction;
                                $ff( $this, $error );
                        }
                } else {
-                       wfEmergencyAbort( $this, $error );
+                       # New method
+                       wfLogDBError( "Connection error: $error\n" );
+                       throw new DBConnectionError( $this, $error );
                }
        }
 
@@ -447,29 +642,13 @@ class Database {
 
                if( $ignore || $tempIgnore ) {
                        wfDebug("SQL ERROR (ignored): $error\n");
+                       $this->ignoreErrors( $ignore );
                } else {
                        $sql1line = str_replace( "\n", "\\n", $sql );
                        wfLogDBError("$fname\t{$this->mServer}\t$errno\t$error\t$sql1line\n");
                        wfDebug("SQL ERROR: " . $error . "\n");
-                       if ( $wgCommandLineMode || !$this->mOut || empty( $wgFullyInitialised ) ) {
-                               $message = "A database error has occurred\n" .
-                                 "Query: $sql\n" .
-                                 "Function: $fname\n" .
-                                 "Error: $errno $error\n";
-                               if ( !$wgCommandLineMode ) {
-                                       $message = nl2br( $message );
-                               }
-                               if( $wgCommandLineMode && $wgColorErrors && !wfIsWindows() && posix_isatty(1) ) {
-                                       $color = 31; // bright red!
-                                       $message = "\x1b[1;{$color}m{$message}\x1b[0m";
-                               }
-                               wfDebugDieBacktrace( $message );
-                       } else {
-                               // this calls wfAbruptExit()
-                               $this->mOut->databaseError( $fname, $sql, $error, $errno );
-                       }
+                       throw new DBQueryError( $this, $error, $errno, $sql, $fname );
                }
-               $this->ignoreErrors( $ignore );
        }
 
 
@@ -561,9 +740,9 @@ class Database {
                        case '!': return $arg;
                        case '&':
                                # return $this->addQuotes( file_get_contents( $arg ) );
-                               wfDebugDieBacktrace( '& mode is not implemented. If it\'s really needed, uncomment the line above.' );
+                               throw new DBUnexpectedError( $this, '& mode is not implemented. If it\'s really needed, uncomment the line above.' );
                        default:
-                               wfDebugDieBacktrace( 'Received invalid match. This should never happen!' );
+                               throw new DBUnexpectedError( $this, 'Received invalid match. This should never happen!' );
                }
        }
 
@@ -575,7 +754,7 @@ class Database {
         */
        function freeResult( $res ) {
                if ( !@/**/mysql_free_result( $res ) ) {
-                       wfDebugDieBacktrace( "Unable to free MySQL result\n" );
+                       throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
                }
        }
 
@@ -585,7 +764,7 @@ class Database {
        function fetchObject( $res ) {
                @/**/$row = mysql_fetch_object( $res );
                if( mysql_errno() ) {
-                       wfDebugDieBacktrace( 'Error in fetchObject(): ' . htmlspecialchars( mysql_error() ) );
+                       throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( mysql_error() ) );
                }
                return $row;
        }
@@ -597,7 +776,7 @@ class Database {
        function fetchRow( $res ) {
                @/**/$row = mysql_fetch_array( $res );
                if (mysql_errno() ) {
-                       wfDebugDieBacktrace( 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) );
+                       throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) );
                }
                return $row;
        }
@@ -608,7 +787,7 @@ class Database {
        function numRows( $res ) {
                @/**/$n = mysql_num_rows( $res );
                if( mysql_errno() ) {
-                       wfDebugDieBacktrace( 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) );
+                       throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) );
                }
                return $n;
        }
@@ -847,7 +1026,7 @@ class Database {
         * @param string $sql A SQL Query
         * @static
         */
-       function generalizeSQL( $sql ) {
+       static function generalizeSQL( $sql ) {
                # This does the same as the regexp below would do, but in such a way
                # as to avoid crashing php on some large strings.
                # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql);
@@ -1079,7 +1258,7 @@ class Database {
         */
        function makeList( $a, $mode = LIST_COMMA ) {
                if ( !is_array( $a ) ) {
-                       wfDebugDieBacktrace( 'Database::makeList called with incorrect parameters' );
+                       throw new DBUnexpectedError( $this, 'Database::makeList called with incorrect parameters' );
                }
 
                $first = true;
@@ -1282,7 +1461,7 @@ class Database {
         */
        function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'Database::deleteJoin' ) {
                if ( !$conds ) {
-                       wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' );
+                       throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' );
                }
 
                $delTable = $this->tableName( $delTable );
@@ -1327,7 +1506,7 @@ class Database {
         */
        function delete( $table, $conds, $fname = 'Database::delete' ) {
                if ( !$conds ) {
-                       wfDebugDieBacktrace( 'Database::delete() called with no conditions' );
+                       throw new DBUnexpectedError( $this, 'Database::delete() called with no conditions' );
                }
                $table = $this->tableName( $table );
                $sql = "DELETE FROM $table";
@@ -1379,7 +1558,7 @@ class Database {
         */
        function limitResult($sql, $limit, $offset=false) {
                if( !is_numeric($limit) ) {
-                       wfDie( "Invalid non-numeric limit passed to limitResult()\n" );
+                       throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
                }
                return " $sql LIMIT "
                                . ( (is_numeric($offset) && $offset != 0) ? "{$offset}," : "" )
@@ -1737,7 +1916,7 @@ class Database {
        /**
         * Replace variables in sourced SQL
         */
-       function replaceVars( $ins ) {
+       protected function replaceVars( $ins ) {
                $varnames = array(
                        'wgDBserver', 'wgDBname', 'wgDBintlname', 'wgDBuser',
                        'wgDBpassword', 'wgDBsqluser', 'wgDBsqlpassword',
@@ -1764,7 +1943,7 @@ class Database {
         * Table name callback
         * @private
         */
-       function tableNameCallback( $matches ) {
+       protected function tableNameCallback( $matches ) {
                return $this->tableName( $matches[1] );
        }
 
index 226bb4d..d5d7379 100644 (file)
@@ -52,7 +52,7 @@ class DatabaseOracle extends Database {
         */
        function open( $server, $user, $password, $dbName ) {
                if ( !function_exists( 'oci_connect' ) ) {
-                       wfDie( "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" );
+                       throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" );
                }
                $this->close();
                $this->mServer = $server;
@@ -137,7 +137,7 @@ class DatabaseOracle extends Database {
 
        function freeResult( $res ) {
                if (!oci_free_statement($res)) {
-                       wfDebugDieBacktrace( "Unable to free Oracle result\n" );
+                       throw new DBUnexpectedError( $this, "Unable to free Oracle result\n" );
                }
                unset($this->mFetchID[$res]);
                unset($this->mFetchCache[$res]);
@@ -385,7 +385,7 @@ class DatabaseOracle extends Database {
        # DELETE where the condition is a join
        function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
                if ( !$conds ) {
-                       wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' );
+                       throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' );
                }
 
                $delTable = $this->tableName( $delTable );
@@ -467,7 +467,7 @@ class DatabaseOracle extends Database {
                        "Query: $sql\n" .
                        "Function: $fname\n" .
                        "Error: $errno $error\n";
-               wfDebugDieBacktrace($message);
+               throw new DBUnexpectedError($this, $message);
        }
 
        /**
index 130a247..23f82c2 100644 (file)
@@ -45,7 +45,7 @@ class DatabasePgsql extends Database {
        function open( $server, $user, $password, $dbName ) {
                # Test for PostgreSQL support, to avoid suppressed fatal error
                if ( !function_exists( 'pg_connect' ) ) {
-                       wfDie( "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" );
+                       throw new DBConnectionError( $this, "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" );
                }
 
                global $wgDBschema;
@@ -101,7 +101,7 @@ class DatabasePgsql extends Database {
 
        function freeResult( $res ) {
                if ( !@pg_free_result( $res ) ) {
-                       wfDebugDieBacktrace( "Unable to free PostgreSQL result\n" );
+                       throw new DBUnexpectedError($this,  "Unable to free PostgreSQL result\n" );
                }
        }
 
@@ -113,7 +113,7 @@ class DatabasePgsql extends Database {
                # hashar : not sure if the following test really trigger if the object
                #          fetching failled.
                if( pg_last_error($this->mConn) ) {
-                       wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
+                       throw new DBUnexpectedError($this,  'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
                }
                return $row;
        }
@@ -121,7 +121,7 @@ class DatabasePgsql extends Database {
        function fetchRow( $res ) {
                @$row = pg_fetch_array( $res );
                if( pg_last_error($this->mConn) ) {
-                       wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
+                       throw new DBUnexpectedError($this,  'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
                }
                return $row;
        }
@@ -129,7 +129,7 @@ class DatabasePgsql extends Database {
        function numRows( $res ) {
                @$n = pg_num_rows( $res );
                if( pg_last_error($this->mConn) ) {
-                       wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
+                       throw new DBUnexpectedError($this,  'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
                }
                return $n;
        }
@@ -183,13 +183,13 @@ class DatabasePgsql extends Database {
        }
 
        function fieldInfo( $table, $field ) {
-               wfDebugDieBacktrace( 'Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre' );
+               throw new DBUnexpectedError($this,  'Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre' );
                /*
                $res = $this->query( "SELECT * FROM '$table' LIMIT 1" );
                $n = pg_num_fields( $res );
                for( $i = 0; $i < $n; $i++ ) {
                        // FIXME
-                       wfDebugDieBacktrace( "Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre" );
+                       throw new DBUnexpectedError($this,  "Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre" );
                        $meta = mysql_fetch_field( $res, $i );
                        if( $field == $meta->name ) {
                                return $meta;
@@ -328,7 +328,7 @@ class DatabasePgsql extends Database {
        # DELETE where the condition is a join
        function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
                if ( !$conds ) {
-                       wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' );
+                       throw new DBUnexpectedError($this,  'Database::deleteJoin() called with empty $conds' );
                }
 
                $delTable = $this->tableName( $delTable );
@@ -404,7 +404,7 @@ class DatabasePgsql extends Database {
                        "Query: $sql\n" .
                        "Function: $fname\n" .
                        "Error: $errno $error\n";
-               wfDebugDieBacktrace($message);
+               throw new DBUnexpectedError($this, $message);
        }
 
        /**
diff --git a/includes/Exception.php b/includes/Exception.php
new file mode 100644 (file)
index 0000000..11bb597
--- /dev/null
@@ -0,0 +1,172 @@
+<?php\r
+\r
+class MWException extends Exception\r
+{\r
+       function useOutputPage() {\r
+               return !empty( $GLOBALS['wgFullyInitialised'] );\r
+       }\r
+\r
+       function useMessageCache() {\r
+               global $wgLang;\r
+               return is_object( $wgLang );\r
+       }\r
+\r
+       function msg( $key, $fallback /*[, params...] */ ) {\r
+               $args = array_slice( func_get_args(), 2 );\r
+               if ( $this->useMessageCache() ) {\r
+                       return wfMsgReal( $key, $args );\r
+               } else {\r
+                       return wfMsgReplaceArgs( $fallback, $args );\r
+               }\r
+       }\r
+                       \r
+       function getHTML() {\r
+               return '<p>' . htmlspecialchars( $this->getMessage() ) . \r
+                       '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .\r
+                       "</p>\n";\r
+       }\r
+\r
+       function getText() {\r
+               return $this->getMessage() .  \r
+                       "\nBacktrace:\n" . $this->getTraceAsString() . "\n";\r
+       }\r
+       \r
+       function getPageTitle() {\r
+               if ( $this->useMessageCache() ) {\r
+                       return wfMsg( 'internalerror' );\r
+               } else {\r
+                       global $wgSitename;\r
+                       return "$wgSitename error";\r
+               }\r
+       }\r
+       \r
+       function reportHTML() {\r
+               global $wgOut;\r
+               if ( $this->useOutputPage() ) {\r
+                       $wgOut->setPageTitle( $this->getPageTitle() );\r
+                       $wgOut->setRobotpolicy( "noindex,nofollow" );\r
+                       $wgOut->setArticleRelated( false );\r
+                       $wgOut->enableClientCache( false );\r
+                       $wgOut->redirect( '' );\r
+                       $wgOut->clearHTML();\r
+                       $wgOut->addHTML( $this->getHTML() );\r
+                       $wgOut->output();\r
+               } else {\r
+                       echo $this->htmlHeader();\r
+                       echo $this->getHTML();\r
+                       echo $this->htmlFooter();\r
+               }\r
+       }\r
+       \r
+       function reportText() {\r
+               echo $this->getText();\r
+       }\r
+\r
+       function report() {\r
+               global $wgCommandLineMode;\r
+               if ( $wgCommandLineMode ) {\r
+                       $this->reportText();\r
+               } else {\r
+                       $this->reportHTML();\r
+               }\r
+       }\r
+\r
+       function htmlHeader() {\r
+               global $wgLogo, $wgSitename, $wgOutputEncoding;\r
+\r
+               if ( !headers_sent() ) {\r
+                       header( 'HTTP/1.0 500 Internal Server Error' );\r
+                       header( 'Content-type: text/html; charset='.$wgOutputEncoding );\r
+                       /* Don't cache error pages!  They cause no end of trouble... */\r
+                       header( 'Cache-control: none' );\r
+                       header( 'Pragma: nocache' );\r
+               }\r
+               $title = $this->getPageTitle();\r
+               echo "<html>\r
+               <head>\r
+               <title>$title</title>\r
+               </head>\r
+               <body>\r
+               <h1><img src='$wgLogo' style='float:left;margin-right:1em' alt=''>$title</h1>\r
+               ";\r
+       }\r
+\r
+       function htmlFooter() {\r
+               echo "</body></html>";\r
+       }               \r
+}\r
+\r
+/**\r
+ * Exception class which takes an HTML error message, and does not \r
+ * produce a backtrace. Replacement for OutputPage::fatalError().\r
+ */\r
+class FatalError extends MWException {\r
+       function getHTML() {\r
+               return $this->getMessage();\r
+       }\r
+\r
+       function getText() {\r
+               return $this->getMessage();\r
+       }\r
+}\r
+\r
+/**\r
+ * Install an exception handler for MediaWiki exception types.\r
+ */\r
+function wfInstallExceptionHandler() {\r
+       set_exception_handler( 'wfExceptionHandler' );\r
+}\r
+\r
+/**\r
+ * Report an exception to the user\r
+ */\r
+function wfReportException( Exception $e ) {\r
+        if ( is_a( $e, 'MWException' ) ) {\r
+                try {\r
+                        $e->report();\r
+                } catch ( Exception $e2 ) {\r
+                        // Exception occurred from within exception handler\r
+                        // Show a simpler error message for the original exception, \r
+                        // don't try to invoke report()\r
+                        $message = "MediaWiki internal error.\n\n" .\r
+                        "Original exception: " . $e->__toString() . \r
+                        "\n\nException caught inside exception handler: " . \r
+                        $e2->__toString() . "\n";\r
+\r
+                        if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {\r
+                                echo $message;\r
+                        } else {\r
+                                echo nl2br( htmlspecialchars( $message ) ). "\n";\r
+                        }\r
+                }\r
+        } else {\r
+                echo $e->__toString();\r
+        }\r
+}\r
+\r
+/**\r
+ * Exception handler which simulates the appropriate catch() handling:\r
+ * \r
+ *   try {\r
+ *       ...\r
+ *   } catch ( MWException $e ) {\r
+ * \r
+ *       $e->report();\r
+ *   } catch ( Exception $e ) {\r
+ *       echo $e->__toString();\r
+ *   }\r
+ */\r
+function wfExceptionHandler( $e ) {\r
+       wfReportException( $e );\r
+       \r
+       // Final cleanup, similar to wfErrorExit()\r
+       try {\r
+               wfProfileClose();\r
+               logProfilingData();\r
+       } catch ( Exception $e ) {}\r
+\r
+       // Exit value should be nonzero for the benefit of shell jobs\r
+       exit( 1 );\r
+}\r
+\r
+?>\r
index d77cf3f..dada8d4 100644 (file)
@@ -436,7 +436,7 @@ class LoadBalancer {
         */
        function reallyOpenConnection( &$server ) {
                if( !is_array( $server ) ) {
-                       wfDebugDieBacktrace( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' );
+                       throw new MWException( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' );
                }
 
                extract( $server );
@@ -477,7 +477,8 @@ class LoadBalancer {
                                } else {
                                        $conn->failFunction( false );
                                }
-                               $conn->reportConnectionError( "{$this->mLastError} ({$conn->mServer})" );
+                               $server = $conn->getProperty( 'mServer' );
+                               $conn->reportConnectionError( "{$this->mLastError} ({$server})" );
                        }
                        $reporting = false;
                }
index 4230226..b5480d6 100644 (file)
@@ -43,6 +43,12 @@ if ( function_exists( 'wfProfileIn' ) ) {
 
 $fname = 'Setup.php';
 wfProfileIn( $fname );
+
+wfProfileIn( $fname.'-exception' );
+require_once( 'Exception.php' );
+wfInstallExceptionHandler();
+wfProfileOut( $fname.'-exception' );
+
 wfProfileIn( $fname.'-includes' );
 
 require_once( 'AutoLoader.php' );