Index: trunk/phase3/includes/DatabaseOracle.php |
— | — | @@ -52,7 +52,7 @@ |
53 | 53 | */ |
54 | 54 | function open( $server, $user, $password, $dbName ) { |
55 | 55 | if ( !function_exists( 'oci_connect' ) ) { |
56 | | - wfDie( "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" ); |
| 56 | + throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" ); |
57 | 57 | } |
58 | 58 | $this->close(); |
59 | 59 | $this->mServer = $server; |
— | — | @@ -137,7 +137,7 @@ |
138 | 138 | |
139 | 139 | function freeResult( $res ) { |
140 | 140 | if (!oci_free_statement($res)) { |
141 | | - wfDebugDieBacktrace( "Unable to free Oracle result\n" ); |
| 141 | + throw new DBUnexpectedError( $this, "Unable to free Oracle result\n" ); |
142 | 142 | } |
143 | 143 | unset($this->mFetchID[$res]); |
144 | 144 | unset($this->mFetchCache[$res]); |
— | — | @@ -385,7 +385,7 @@ |
386 | 386 | # DELETE where the condition is a join |
387 | 387 | function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { |
388 | 388 | if ( !$conds ) { |
389 | | - wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); |
| 389 | + throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' ); |
390 | 390 | } |
391 | 391 | |
392 | 392 | $delTable = $this->tableName( $delTable ); |
— | — | @@ -467,7 +467,7 @@ |
468 | 468 | "Query: $sql\n" . |
469 | 469 | "Function: $fname\n" . |
470 | 470 | "Error: $errno $error\n"; |
471 | | - wfDebugDieBacktrace($message); |
| 471 | + throw new DBUnexpectedError($this, $message); |
472 | 472 | } |
473 | 473 | |
474 | 474 | /** |
Index: trunk/phase3/includes/Setup.php |
— | — | @@ -43,6 +43,12 @@ |
44 | 44 | |
45 | 45 | $fname = 'Setup.php'; |
46 | 46 | wfProfileIn( $fname ); |
| 47 | + |
| 48 | +wfProfileIn( $fname.'-exception' ); |
| 49 | +require_once( 'Exception.php' ); |
| 50 | +wfInstallExceptionHandler(); |
| 51 | +wfProfileOut( $fname.'-exception' ); |
| 52 | + |
47 | 53 | wfProfileIn( $fname.'-includes' ); |
48 | 54 | |
49 | 55 | require_once( 'AutoLoader.php' ); |
Index: trunk/phase3/includes/DatabasePostgreSQL.php |
— | — | @@ -45,7 +45,7 @@ |
46 | 46 | function open( $server, $user, $password, $dbName ) { |
47 | 47 | # Test for PostgreSQL support, to avoid suppressed fatal error |
48 | 48 | if ( !function_exists( 'pg_connect' ) ) { |
49 | | - wfDie( "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" ); |
| 49 | + throw new DBConnectionError( $this, "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" ); |
50 | 50 | } |
51 | 51 | |
52 | 52 | global $wgDBschema; |
— | — | @@ -101,7 +101,7 @@ |
102 | 102 | |
103 | 103 | function freeResult( $res ) { |
104 | 104 | if ( !@pg_free_result( $res ) ) { |
105 | | - wfDebugDieBacktrace( "Unable to free PostgreSQL result\n" ); |
| 105 | + throw new DBUnexpectedError($this, "Unable to free PostgreSQL result\n" ); |
106 | 106 | } |
107 | 107 | } |
108 | 108 | |
— | — | @@ -113,7 +113,7 @@ |
114 | 114 | # hashar : not sure if the following test really trigger if the object |
115 | 115 | # fetching failled. |
116 | 116 | if( pg_last_error($this->mConn) ) { |
117 | | - wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
| 117 | + throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
118 | 118 | } |
119 | 119 | return $row; |
120 | 120 | } |
— | — | @@ -121,7 +121,7 @@ |
122 | 122 | function fetchRow( $res ) { |
123 | 123 | @$row = pg_fetch_array( $res ); |
124 | 124 | if( pg_last_error($this->mConn) ) { |
125 | | - wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
| 125 | + throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
126 | 126 | } |
127 | 127 | return $row; |
128 | 128 | } |
— | — | @@ -129,7 +129,7 @@ |
130 | 130 | function numRows( $res ) { |
131 | 131 | @$n = pg_num_rows( $res ); |
132 | 132 | if( pg_last_error($this->mConn) ) { |
133 | | - wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
| 133 | + throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); |
134 | 134 | } |
135 | 135 | return $n; |
136 | 136 | } |
— | — | @@ -183,13 +183,13 @@ |
184 | 184 | } |
185 | 185 | |
186 | 186 | function fieldInfo( $table, $field ) { |
187 | | - wfDebugDieBacktrace( 'Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre' ); |
| 187 | + throw new DBUnexpectedError($this, 'Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre' ); |
188 | 188 | /* |
189 | 189 | $res = $this->query( "SELECT * FROM '$table' LIMIT 1" ); |
190 | 190 | $n = pg_num_fields( $res ); |
191 | 191 | for( $i = 0; $i < $n; $i++ ) { |
192 | 192 | // FIXME |
193 | | - wfDebugDieBacktrace( "Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre" ); |
| 193 | + throw new DBUnexpectedError($this, "Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre" ); |
194 | 194 | $meta = mysql_fetch_field( $res, $i ); |
195 | 195 | if( $field == $meta->name ) { |
196 | 196 | return $meta; |
— | — | @@ -328,7 +328,7 @@ |
329 | 329 | # DELETE where the condition is a join |
330 | 330 | function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { |
331 | 331 | if ( !$conds ) { |
332 | | - wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); |
| 332 | + throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' ); |
333 | 333 | } |
334 | 334 | |
335 | 335 | $delTable = $this->tableName( $delTable ); |
— | — | @@ -404,7 +404,7 @@ |
405 | 405 | "Query: $sql\n" . |
406 | 406 | "Function: $fname\n" . |
407 | 407 | "Error: $errno $error\n"; |
408 | | - wfDebugDieBacktrace($message); |
| 408 | + throw new DBUnexpectedError($this, $message); |
409 | 409 | } |
410 | 410 | |
411 | 411 | /** |
Index: trunk/phase3/includes/LoadBalancer.php |
— | — | @@ -436,7 +436,7 @@ |
437 | 437 | */ |
438 | 438 | function reallyOpenConnection( &$server ) { |
439 | 439 | if( !is_array( $server ) ) { |
440 | | - wfDebugDieBacktrace( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' ); |
| 440 | + throw new MWException( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' ); |
441 | 441 | } |
442 | 442 | |
443 | 443 | extract( $server ); |
— | — | @@ -477,7 +477,8 @@ |
478 | 478 | } else { |
479 | 479 | $conn->failFunction( false ); |
480 | 480 | } |
481 | | - $conn->reportConnectionError( "{$this->mLastError} ({$conn->mServer})" ); |
| 481 | + $server = $conn->getProperty( 'mServer' ); |
| 482 | + $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); |
482 | 483 | } |
483 | 484 | $reporting = false; |
484 | 485 | } |
Index: trunk/phase3/includes/Exception.php |
— | — | @@ -0,0 +1,172 @@ |
| 2 | +<?php
|
| 3 | +
|
| 4 | +class MWException extends Exception
|
| 5 | +{
|
| 6 | + function useOutputPage() {
|
| 7 | + return !empty( $GLOBALS['wgFullyInitialised'] );
|
| 8 | + }
|
| 9 | +
|
| 10 | + function useMessageCache() {
|
| 11 | + global $wgLang;
|
| 12 | + return is_object( $wgLang );
|
| 13 | + }
|
| 14 | +
|
| 15 | + function msg( $key, $fallback /*[, params...] */ ) {
|
| 16 | + $args = array_slice( func_get_args(), 2 );
|
| 17 | + if ( $this->useMessageCache() ) {
|
| 18 | + return wfMsgReal( $key, $args );
|
| 19 | + } else {
|
| 20 | + return wfMsgReplaceArgs( $fallback, $args );
|
| 21 | + }
|
| 22 | + }
|
| 23 | +
|
| 24 | + function getHTML() {
|
| 25 | + return '<p>' . htmlspecialchars( $this->getMessage() ) .
|
| 26 | + '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
|
| 27 | + "</p>\n";
|
| 28 | + }
|
| 29 | +
|
| 30 | + function getText() {
|
| 31 | + return $this->getMessage() .
|
| 32 | + "\nBacktrace:\n" . $this->getTraceAsString() . "\n";
|
| 33 | + }
|
| 34 | +
|
| 35 | + function getPageTitle() {
|
| 36 | + if ( $this->useMessageCache() ) {
|
| 37 | + return wfMsg( 'internalerror' );
|
| 38 | + } else {
|
| 39 | + global $wgSitename;
|
| 40 | + return "$wgSitename error";
|
| 41 | + }
|
| 42 | + }
|
| 43 | +
|
| 44 | + function reportHTML() {
|
| 45 | + global $wgOut;
|
| 46 | + if ( $this->useOutputPage() ) {
|
| 47 | + $wgOut->setPageTitle( $this->getPageTitle() );
|
| 48 | + $wgOut->setRobotpolicy( "noindex,nofollow" );
|
| 49 | + $wgOut->setArticleRelated( false );
|
| 50 | + $wgOut->enableClientCache( false );
|
| 51 | + $wgOut->redirect( '' );
|
| 52 | + $wgOut->clearHTML();
|
| 53 | + $wgOut->addHTML( $this->getHTML() );
|
| 54 | + $wgOut->output();
|
| 55 | + } else {
|
| 56 | + echo $this->htmlHeader();
|
| 57 | + echo $this->getHTML();
|
| 58 | + echo $this->htmlFooter();
|
| 59 | + }
|
| 60 | + }
|
| 61 | +
|
| 62 | + function reportText() {
|
| 63 | + echo $this->getText();
|
| 64 | + }
|
| 65 | +
|
| 66 | + function report() {
|
| 67 | + global $wgCommandLineMode;
|
| 68 | + if ( $wgCommandLineMode ) {
|
| 69 | + $this->reportText();
|
| 70 | + } else {
|
| 71 | + $this->reportHTML();
|
| 72 | + }
|
| 73 | + }
|
| 74 | +
|
| 75 | + function htmlHeader() {
|
| 76 | + global $wgLogo, $wgSitename, $wgOutputEncoding;
|
| 77 | +
|
| 78 | + if ( !headers_sent() ) {
|
| 79 | + header( 'HTTP/1.0 500 Internal Server Error' );
|
| 80 | + header( 'Content-type: text/html; charset='.$wgOutputEncoding );
|
| 81 | + /* Don't cache error pages! They cause no end of trouble... */
|
| 82 | + header( 'Cache-control: none' );
|
| 83 | + header( 'Pragma: nocache' );
|
| 84 | + }
|
| 85 | + $title = $this->getPageTitle();
|
| 86 | + echo "<html>
|
| 87 | + <head>
|
| 88 | + <title>$title</title>
|
| 89 | + </head>
|
| 90 | + <body>
|
| 91 | + <h1><img src='$wgLogo' style='float:left;margin-right:1em' alt=''>$title</h1>
|
| 92 | + ";
|
| 93 | + }
|
| 94 | +
|
| 95 | + function htmlFooter() {
|
| 96 | + echo "</body></html>";
|
| 97 | + }
|
| 98 | +}
|
| 99 | +
|
| 100 | +/**
|
| 101 | + * Exception class which takes an HTML error message, and does not
|
| 102 | + * produce a backtrace. Replacement for OutputPage::fatalError().
|
| 103 | + */
|
| 104 | +class FatalError extends MWException {
|
| 105 | + function getHTML() {
|
| 106 | + return $this->getMessage();
|
| 107 | + }
|
| 108 | +
|
| 109 | + function getText() {
|
| 110 | + return $this->getMessage();
|
| 111 | + }
|
| 112 | +}
|
| 113 | +
|
| 114 | +/**
|
| 115 | + * Install an exception handler for MediaWiki exception types.
|
| 116 | + */
|
| 117 | +function wfInstallExceptionHandler() {
|
| 118 | + set_exception_handler( 'wfExceptionHandler' );
|
| 119 | +}
|
| 120 | +
|
| 121 | +/**
|
| 122 | + * Report an exception to the user
|
| 123 | + */
|
| 124 | +function wfReportException( Exception $e ) {
|
| 125 | + if ( is_a( $e, 'MWException' ) ) {
|
| 126 | + try {
|
| 127 | + $e->report();
|
| 128 | + } catch ( Exception $e2 ) {
|
| 129 | + // Exception occurred from within exception handler
|
| 130 | + // Show a simpler error message for the original exception,
|
| 131 | + // don't try to invoke report()
|
| 132 | + $message = "MediaWiki internal error.\n\n" .
|
| 133 | + "Original exception: " . $e->__toString() .
|
| 134 | + "\n\nException caught inside exception handler: " .
|
| 135 | + $e2->__toString() . "\n";
|
| 136 | +
|
| 137 | + if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
|
| 138 | + echo $message;
|
| 139 | + } else {
|
| 140 | + echo nl2br( htmlspecialchars( $message ) ). "\n";
|
| 141 | + }
|
| 142 | + }
|
| 143 | + } else {
|
| 144 | + echo $e->__toString();
|
| 145 | + }
|
| 146 | +}
|
| 147 | +
|
| 148 | +/**
|
| 149 | + * Exception handler which simulates the appropriate catch() handling:
|
| 150 | + *
|
| 151 | + * try {
|
| 152 | + * ...
|
| 153 | + * } catch ( MWException $e ) {
|
| 154 | + *
|
| 155 | + * $e->report();
|
| 156 | + * } catch ( Exception $e ) {
|
| 157 | + * echo $e->__toString();
|
| 158 | + * }
|
| 159 | + */
|
| 160 | +function wfExceptionHandler( $e ) {
|
| 161 | + wfReportException( $e );
|
| 162 | +
|
| 163 | + // Final cleanup, similar to wfErrorExit()
|
| 164 | + try {
|
| 165 | + wfProfileClose();
|
| 166 | + logProfilingData();
|
| 167 | + } catch ( Exception $e ) {}
|
| 168 | +
|
| 169 | + // Exit value should be nonzero for the benefit of shell jobs
|
| 170 | + exit( 1 );
|
| 171 | +}
|
| 172 | +
|
| 173 | +?>
|
Index: trunk/phase3/includes/Database.php |
— | — | @@ -24,8 +24,12 @@ |
25 | 25 | /** Maximum time to wait before retry */ |
26 | 26 | define( 'DEADLOCK_DELAY_MAX', 1500000 ); |
27 | 27 | |
| 28 | +/****************************************************************************** |
| 29 | + * Utility classes |
| 30 | + *****************************************************************************/ |
| 31 | + |
28 | 32 | class DBObject { |
29 | | - var $mData; |
| 33 | + public $mData; |
30 | 34 | |
31 | 35 | function DBObject($data) { |
32 | 36 | $this->mData = $data; |
— | — | @@ -40,7 +44,191 @@ |
41 | 45 | } |
42 | 46 | }; |
43 | 47 | |
| 48 | +/****************************************************************************** |
| 49 | + * Error classes |
| 50 | + *****************************************************************************/ |
| 51 | + |
44 | 52 | /** |
| 53 | + * Database error base class |
| 54 | + */ |
| 55 | +class DBError extends MWException { |
| 56 | + public $db; |
| 57 | + |
| 58 | + /** |
| 59 | + * Construct a database error |
| 60 | + * @param Database $db The database object which threw the error |
| 61 | + * @param string $error A simple error message to be used for debugging |
| 62 | + */ |
| 63 | + function __construct( Database &$db, $error ) { |
| 64 | + $this->db =& $db; |
| 65 | + parent::__construct( $error ); |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +class DBConnectionError extends DBError { |
| 70 | + public $error; |
| 71 | + |
| 72 | + function __construct( Database &$db, $error = 'unknown error' ) { |
| 73 | + $msg = 'DB connection error'; |
| 74 | + if ( trim( $error ) != '' ) { |
| 75 | + $msg .= ": $error"; |
| 76 | + } |
| 77 | + $this->error = $error; |
| 78 | + parent::__construct( $db, $msg ); |
| 79 | + } |
| 80 | + |
| 81 | + function useOutputPage() { |
| 82 | + // Not likely to work |
| 83 | + return false; |
| 84 | + } |
| 85 | + |
| 86 | + function useMessageCache() { |
| 87 | + // Not likely to work |
| 88 | + return false; |
| 89 | + } |
| 90 | + |
| 91 | + function getText() { |
| 92 | + return $this->getMessage() . "\n"; |
| 93 | + } |
| 94 | + |
| 95 | + function getPageTitle() { |
| 96 | + global $wgSitename; |
| 97 | + return "$wgSitename has a problem"; |
| 98 | + } |
| 99 | + |
| 100 | + function getHTML() { |
| 101 | + global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgOutputEncoding; |
| 102 | + global $wgSitename, $wgServer, $wgMessageCache, $wgLogo; |
| 103 | + |
| 104 | + # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky. |
| 105 | + # Hard coding strings instead. |
| 106 | + |
| 107 | + $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>"; |
| 108 | + $mainpage = 'Main Page'; |
| 109 | + $searchdisabled = <<<EOT |
| 110 | +<p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime. |
| 111 | +<span style="font-size: 89%; display: block; margin-left: .2em">Note that their indexes of $wgSitename content may be out of date.</span></p>', |
| 112 | +EOT; |
| 113 | + |
| 114 | + $googlesearch = " |
| 115 | +<!-- SiteSearch Google --> |
| 116 | +<FORM method=GET action=\"http://www.google.com/search\"> |
| 117 | +<TABLE bgcolor=\"#FFFFFF\"><tr><td> |
| 118 | +<A HREF=\"http://www.google.com/\"> |
| 119 | +<IMG SRC=\"http://www.google.com/logos/Logo_40wht.gif\" |
| 120 | +border=\"0\" ALT=\"Google\"></A> |
| 121 | +</td> |
| 122 | +<td> |
| 123 | +<INPUT TYPE=text name=q size=31 maxlength=255 value=\"$1\"> |
| 124 | +<INPUT type=submit name=btnG VALUE=\"Google Search\"> |
| 125 | +<font size=-1> |
| 126 | +<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 /> |
| 127 | +<input type='hidden' name='ie' value='$2'> |
| 128 | +<input type='hidden' name='oe' value='$2'> |
| 129 | +</font> |
| 130 | +</td></tr></TABLE> |
| 131 | +</FORM> |
| 132 | +<!-- SiteSearch Google -->"; |
| 133 | + $cachederror = "The following is a cached copy of the requested page, and may not be up to date. "; |
| 134 | + |
| 135 | + # No database access |
| 136 | + if ( is_object( $wgMessageCache ) ) { |
| 137 | + $wgMessageCache->disable(); |
| 138 | + } |
| 139 | + |
| 140 | + if ( trim( $this->error ) == '' ) { |
| 141 | + $this->error = $this->db->getProperty('mServer'); |
| 142 | + } |
| 143 | + |
| 144 | + $text = str_replace( '$1', $this->error, $noconnect ); |
| 145 | + $text .= wfGetSiteNotice(); |
| 146 | + |
| 147 | + if($wgUseFileCache) { |
| 148 | + if($wgTitle) { |
| 149 | + $t =& $wgTitle; |
| 150 | + } else { |
| 151 | + if($title) { |
| 152 | + $t = Title::newFromURL( $title ); |
| 153 | + } elseif (@/**/$_REQUEST['search']) { |
| 154 | + $search = $_REQUEST['search']; |
| 155 | + return $searchdisabled . |
| 156 | + str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ), |
| 157 | + $wgInputEncoding ), $googlesearch ); |
| 158 | + } else { |
| 159 | + $t = Title::newFromText( $mainpage ); |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + $cache = new CacheManager( $t ); |
| 164 | + if( $cache->isFileCached() ) { |
| 165 | + $msg = '<p style="color: red"><b>'.$msg."<br />\n" . |
| 166 | + $cachederror . "</b></p>\n"; |
| 167 | + |
| 168 | + $tag = '<div id="article">'; |
| 169 | + $text = str_replace( |
| 170 | + $tag, |
| 171 | + $tag . $msg, |
| 172 | + $cache->fetchPageText() ); |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + return $text; |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +class DBQueryError extends DBError { |
| 181 | + public $error, $errno, $sql, $fname; |
| 182 | + |
| 183 | + function __construct( Database &$db, $error, $errno, $sql, $fname ) { |
| 184 | + $message = "A database error has occurred\n" . |
| 185 | + "Query: $sql\n" . |
| 186 | + "Function: $fname\n" . |
| 187 | + "Error: $errno $error\n"; |
| 188 | + |
| 189 | + parent::__construct( $db, $message ); |
| 190 | + $this->error = $error; |
| 191 | + $this->errno = $errno; |
| 192 | + $this->sql = $sql; |
| 193 | + $this->fname = $fname; |
| 194 | + } |
| 195 | + |
| 196 | + function getText() { |
| 197 | + if ( $this->useMessageCache() ) { |
| 198 | + return wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ), |
| 199 | + htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n"; |
| 200 | + } else { |
| 201 | + return $this->getMessage(); |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + function getSQL() { |
| 206 | + global $wgShowSQLErrors; |
| 207 | + if( !$wgShowSQLErrors ) { |
| 208 | + return $this->msg( 'sqlhidden', 'SQL hidden' ); |
| 209 | + } else { |
| 210 | + return $this->sql; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + function getPageTitle() { |
| 215 | + return $this->msg( 'databaseerror', 'Database error' ); |
| 216 | + } |
| 217 | + |
| 218 | + function getHTML() { |
| 219 | + if ( $this->useMessageCache() ) { |
| 220 | + return wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ), |
| 221 | + htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ); |
| 222 | + } else { |
| 223 | + return nl2br( htmlspecialchars( $this->getMessage() ) ); |
| 224 | + } |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +class DBUnexpectedError extends DBError {} |
| 229 | + |
| 230 | +/******************************************************************************/ |
| 231 | + |
| 232 | +/** |
45 | 233 | * Database abstraction object |
46 | 234 | * @package MediaWiki |
47 | 235 | */ |
— | — | @@ -49,22 +237,19 @@ |
50 | 238 | #------------------------------------------------------------------------------ |
51 | 239 | # Variables |
52 | 240 | #------------------------------------------------------------------------------ |
53 | | - /**#@+ |
54 | | - * @private |
55 | | - */ |
56 | | - var $mLastQuery = ''; |
57 | 241 | |
58 | | - var $mServer, $mUser, $mPassword, $mConn = null, $mDBname; |
59 | | - var $mOut, $mOpened = false; |
| 242 | + protected $mLastQuery = ''; |
60 | 243 | |
61 | | - var $mFailFunction; |
62 | | - var $mTablePrefix; |
63 | | - var $mFlags; |
64 | | - var $mTrxLevel = 0; |
65 | | - var $mErrorCount = 0; |
66 | | - var $mLBInfo = array(); |
67 | | - /**#@-*/ |
| 244 | + protected $mServer, $mUser, $mPassword, $mConn = null, $mDBname; |
| 245 | + protected $mOut, $mOpened = false; |
68 | 246 | |
| 247 | + protected $mFailFunction; |
| 248 | + protected $mTablePrefix; |
| 249 | + protected $mFlags; |
| 250 | + protected $mTrxLevel = 0; |
| 251 | + protected $mErrorCount = 0; |
| 252 | + protected $mLBInfo = array(); |
| 253 | + |
69 | 254 | #------------------------------------------------------------------------------ |
70 | 255 | # Accessors |
71 | 256 | #------------------------------------------------------------------------------ |
— | — | @@ -173,6 +358,13 @@ |
174 | 359 | return !!($this->mFlags & $flag); |
175 | 360 | } |
176 | 361 | |
| 362 | + /** |
| 363 | + * General read-only accessor |
| 364 | + */ |
| 365 | + function getProperty( $name ) { |
| 366 | + return $this->$name; |
| 367 | + } |
| 368 | + |
177 | 369 | #------------------------------------------------------------------------------ |
178 | 370 | # Other functions |
179 | 371 | #------------------------------------------------------------------------------ |
— | — | @@ -189,7 +381,7 @@ |
190 | 382 | * @param $flags |
191 | 383 | * @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php |
192 | 384 | */ |
193 | | - function Database( $server = false, $user = false, $password = false, $dbName = false, |
| 385 | + function __construct( $server = false, $user = false, $password = false, $dbName = false, |
194 | 386 | $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { |
195 | 387 | |
196 | 388 | global $wgOut, $wgDBprefix, $wgCommandLineMode; |
— | — | @@ -234,7 +426,7 @@ |
235 | 427 | * @param failFunction |
236 | 428 | * @param $flags |
237 | 429 | */ |
238 | | - function newFromParams( $server, $user, $password, $dbName, |
| 430 | + static function newFromParams( $server, $user, $password, $dbName, |
239 | 431 | $failFunction = false, $flags = 0 ) |
240 | 432 | { |
241 | 433 | return new Database( $server, $user, $password, $dbName, $failFunction, $flags ); |
— | — | @@ -253,9 +445,10 @@ |
254 | 446 | @dl('mysql.so'); |
255 | 447 | } |
256 | 448 | |
| 449 | + # Fail now |
257 | 450 | # Otherwise we get a suppressed fatal error, which is very hard to track down |
258 | 451 | if ( !function_exists( 'mysql_connect' ) ) { |
259 | | - wfDie( "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" ); |
| 452 | + throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" ); |
260 | 453 | } |
261 | 454 | |
262 | 455 | $this->close(); |
— | — | @@ -328,7 +521,6 @@ |
329 | 522 | } |
330 | 523 | |
331 | 524 | /** |
332 | | - * @private |
333 | 525 | * @param string $error fallback error message, used if none is given by MySQL |
334 | 526 | */ |
335 | 527 | function reportConnectionError( $error = 'Unknown error' ) { |
— | — | @@ -338,12 +530,15 @@ |
339 | 531 | } |
340 | 532 | |
341 | 533 | if ( $this->mFailFunction ) { |
| 534 | + # Legacy error handling method |
342 | 535 | if ( !is_int( $this->mFailFunction ) ) { |
343 | 536 | $ff = $this->mFailFunction; |
344 | 537 | $ff( $this, $error ); |
345 | 538 | } |
346 | 539 | } else { |
347 | | - wfEmergencyAbort( $this, $error ); |
| 540 | + # New method |
| 541 | + wfLogDBError( "Connection error: $error\n" ); |
| 542 | + throw new DBConnectionError( $this, $error ); |
348 | 543 | } |
349 | 544 | } |
350 | 545 | |
— | — | @@ -447,29 +642,13 @@ |
448 | 643 | |
449 | 644 | if( $ignore || $tempIgnore ) { |
450 | 645 | wfDebug("SQL ERROR (ignored): $error\n"); |
| 646 | + $this->ignoreErrors( $ignore ); |
451 | 647 | } else { |
452 | 648 | $sql1line = str_replace( "\n", "\\n", $sql ); |
453 | 649 | wfLogDBError("$fname\t{$this->mServer}\t$errno\t$error\t$sql1line\n"); |
454 | 650 | wfDebug("SQL ERROR: " . $error . "\n"); |
455 | | - if ( $wgCommandLineMode || !$this->mOut || empty( $wgFullyInitialised ) ) { |
456 | | - $message = "A database error has occurred\n" . |
457 | | - "Query: $sql\n" . |
458 | | - "Function: $fname\n" . |
459 | | - "Error: $errno $error\n"; |
460 | | - if ( !$wgCommandLineMode ) { |
461 | | - $message = nl2br( $message ); |
462 | | - } |
463 | | - if( $wgCommandLineMode && $wgColorErrors && !wfIsWindows() && posix_isatty(1) ) { |
464 | | - $color = 31; // bright red! |
465 | | - $message = "\x1b[1;{$color}m{$message}\x1b[0m"; |
466 | | - } |
467 | | - wfDebugDieBacktrace( $message ); |
468 | | - } else { |
469 | | - // this calls wfAbruptExit() |
470 | | - $this->mOut->databaseError( $fname, $sql, $error, $errno ); |
471 | | - } |
| 651 | + throw new DBQueryError( $this, $error, $errno, $sql, $fname ); |
472 | 652 | } |
473 | | - $this->ignoreErrors( $ignore ); |
474 | 653 | } |
475 | 654 | |
476 | 655 | |
— | — | @@ -561,9 +740,9 @@ |
562 | 741 | case '!': return $arg; |
563 | 742 | case '&': |
564 | 743 | # return $this->addQuotes( file_get_contents( $arg ) ); |
565 | | - wfDebugDieBacktrace( '& mode is not implemented. If it\'s really needed, uncomment the line above.' ); |
| 744 | + throw new DBUnexpectedError( $this, '& mode is not implemented. If it\'s really needed, uncomment the line above.' ); |
566 | 745 | default: |
567 | | - wfDebugDieBacktrace( 'Received invalid match. This should never happen!' ); |
| 746 | + throw new DBUnexpectedError( $this, 'Received invalid match. This should never happen!' ); |
568 | 747 | } |
569 | 748 | } |
570 | 749 | |
— | — | @@ -575,7 +754,7 @@ |
576 | 755 | */ |
577 | 756 | function freeResult( $res ) { |
578 | 757 | if ( !@/**/mysql_free_result( $res ) ) { |
579 | | - wfDebugDieBacktrace( "Unable to free MySQL result\n" ); |
| 758 | + throw new DBUnexpectedError( $this, "Unable to free MySQL result" ); |
580 | 759 | } |
581 | 760 | } |
582 | 761 | |
— | — | @@ -585,7 +764,7 @@ |
586 | 765 | function fetchObject( $res ) { |
587 | 766 | @/**/$row = mysql_fetch_object( $res ); |
588 | 767 | if( mysql_errno() ) { |
589 | | - wfDebugDieBacktrace( 'Error in fetchObject(): ' . htmlspecialchars( mysql_error() ) ); |
| 768 | + throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( mysql_error() ) ); |
590 | 769 | } |
591 | 770 | return $row; |
592 | 771 | } |
— | — | @@ -597,7 +776,7 @@ |
598 | 777 | function fetchRow( $res ) { |
599 | 778 | @/**/$row = mysql_fetch_array( $res ); |
600 | 779 | if (mysql_errno() ) { |
601 | | - wfDebugDieBacktrace( 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) ); |
| 780 | + throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) ); |
602 | 781 | } |
603 | 782 | return $row; |
604 | 783 | } |
— | — | @@ -608,7 +787,7 @@ |
609 | 788 | function numRows( $res ) { |
610 | 789 | @/**/$n = mysql_num_rows( $res ); |
611 | 790 | if( mysql_errno() ) { |
612 | | - wfDebugDieBacktrace( 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) ); |
| 791 | + throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) ); |
613 | 792 | } |
614 | 793 | return $n; |
615 | 794 | } |
— | — | @@ -847,7 +1026,7 @@ |
848 | 1027 | * @param string $sql A SQL Query |
849 | 1028 | * @static |
850 | 1029 | */ |
851 | | - function generalizeSQL( $sql ) { |
| 1030 | + static function generalizeSQL( $sql ) { |
852 | 1031 | # This does the same as the regexp below would do, but in such a way |
853 | 1032 | # as to avoid crashing php on some large strings. |
854 | 1033 | # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql); |
— | — | @@ -1079,7 +1258,7 @@ |
1080 | 1259 | */ |
1081 | 1260 | function makeList( $a, $mode = LIST_COMMA ) { |
1082 | 1261 | if ( !is_array( $a ) ) { |
1083 | | - wfDebugDieBacktrace( 'Database::makeList called with incorrect parameters' ); |
| 1262 | + throw new DBUnexpectedError( $this, 'Database::makeList called with incorrect parameters' ); |
1084 | 1263 | } |
1085 | 1264 | |
1086 | 1265 | $first = true; |
— | — | @@ -1282,7 +1461,7 @@ |
1283 | 1462 | */ |
1284 | 1463 | function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'Database::deleteJoin' ) { |
1285 | 1464 | if ( !$conds ) { |
1286 | | - wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); |
| 1465 | + throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' ); |
1287 | 1466 | } |
1288 | 1467 | |
1289 | 1468 | $delTable = $this->tableName( $delTable ); |
— | — | @@ -1327,7 +1506,7 @@ |
1328 | 1507 | */ |
1329 | 1508 | function delete( $table, $conds, $fname = 'Database::delete' ) { |
1330 | 1509 | if ( !$conds ) { |
1331 | | - wfDebugDieBacktrace( 'Database::delete() called with no conditions' ); |
| 1510 | + throw new DBUnexpectedError( $this, 'Database::delete() called with no conditions' ); |
1332 | 1511 | } |
1333 | 1512 | $table = $this->tableName( $table ); |
1334 | 1513 | $sql = "DELETE FROM $table"; |
— | — | @@ -1379,7 +1558,7 @@ |
1380 | 1559 | */ |
1381 | 1560 | function limitResult($sql, $limit, $offset=false) { |
1382 | 1561 | if( !is_numeric($limit) ) { |
1383 | | - wfDie( "Invalid non-numeric limit passed to limitResult()\n" ); |
| 1562 | + throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" ); |
1384 | 1563 | } |
1385 | 1564 | return " $sql LIMIT " |
1386 | 1565 | . ( (is_numeric($offset) && $offset != 0) ? "{$offset}," : "" ) |
— | — | @@ -1737,7 +1916,7 @@ |
1738 | 1917 | /** |
1739 | 1918 | * Replace variables in sourced SQL |
1740 | 1919 | */ |
1741 | | - function replaceVars( $ins ) { |
| 1920 | + protected function replaceVars( $ins ) { |
1742 | 1921 | $varnames = array( |
1743 | 1922 | 'wgDBserver', 'wgDBname', 'wgDBintlname', 'wgDBuser', |
1744 | 1923 | 'wgDBpassword', 'wgDBsqluser', 'wgDBsqlpassword', |
— | — | @@ -1764,7 +1943,7 @@ |
1765 | 1944 | * Table name callback |
1766 | 1945 | * @private |
1767 | 1946 | */ |
1768 | | - function tableNameCallback( $matches ) { |
| 1947 | + protected function tableNameCallback( $matches ) { |
1769 | 1948 | return $this->tableName( $matches[1] ); |
1770 | 1949 | } |
1771 | 1950 | |