MediaWiki  REL1_23
DatabaseError.php
Go to the documentation of this file.
00001 <?php
00028 class DBError extends MWException {
00030     public $db;
00031 
00037     function __construct( DatabaseBase $db = null, $error ) {
00038         $this->db = $db;
00039         parent::__construct( $error );
00040     }
00041 }
00042 
00050 class DBExpectedError extends DBError {
00054     function getText() {
00055         global $wgShowDBErrorBacktrace;
00056 
00057         $s = $this->getTextContent() . "\n";
00058 
00059         if ( $wgShowDBErrorBacktrace ) {
00060             $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
00061         }
00062 
00063         return $s;
00064     }
00065 
00069     function getHTML() {
00070         global $wgShowDBErrorBacktrace;
00071 
00072         $s = $this->getHTMLContent();
00073 
00074         if ( $wgShowDBErrorBacktrace ) {
00075             $s .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
00076         }
00077 
00078         return $s;
00079     }
00080 
00084     protected function getTextContent() {
00085         return $this->getMessage();
00086     }
00087 
00091     protected function getHTMLContent() {
00092         return '<p>' . nl2br( htmlspecialchars( $this->getTextContent() ) ) . '</p>';
00093     }
00094 }
00095 
00099 class DBConnectionError extends DBExpectedError {
00101     public $error;
00102 
00107     function __construct( DatabaseBase $db = null, $error = 'unknown error' ) {
00108         $msg = 'DB connection error';
00109 
00110         if ( trim( $error ) != '' ) {
00111             $msg .= ": $error";
00112         } elseif ( $db ) {
00113             $error = $this->db->getServer();
00114         }
00115 
00116         parent::__construct( $db, $msg );
00117         $this->error = $error;
00118     }
00119 
00123     function useOutputPage() {
00124         // Not likely to work
00125         return false;
00126     }
00127 
00135     function msg( $key, $fallback /*[, params...] */ ) {
00136         global $wgLang;
00137 
00138         $args = array_slice( func_get_args(), 2 );
00139 
00140         if ( $this->useMessageCache() ) {
00141             $message = $wgLang->getMessage( $key );
00142         } else {
00143             $message = $fallback;
00144         }
00145 
00146         return wfMsgReplaceArgs( $message, $args );
00147     }
00148 
00152     function isLoggable() {
00153         // Don't send to the exception log, already in dberror log
00154         return false;
00155     }
00156 
00160     function getHTML() {
00161         global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
00162 
00163         $sorry = htmlspecialchars( $this->msg(
00164             'dberr-problems',
00165             'Sorry! This site is experiencing technical difficulties.'
00166         ) );
00167         $again = htmlspecialchars( $this->msg(
00168             'dberr-again',
00169             'Try waiting a few minutes and reloading.'
00170         ) );
00171 
00172         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00173             $info = str_replace(
00174                 '$1', Html::element( 'span', array( 'dir' => 'ltr' ), $this->error ),
00175                 htmlspecialchars( $this->msg( 'dberr-info', '(Cannot contact the database server: $1)' ) )
00176             );
00177         } else {
00178             $info = htmlspecialchars( $this->msg(
00179                 'dberr-info-hidden',
00180                 '(Cannot contact the database server)'
00181             ) );
00182         }
00183 
00184         # No database access
00185         MessageCache::singleton()->disable();
00186 
00187         $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
00188 
00189         if ( $wgShowDBErrorBacktrace ) {
00190             $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
00191         }
00192 
00193         $html .= '<hr />';
00194         $html .= $this->searchForm();
00195 
00196         return $html;
00197     }
00198 
00199     protected function getTextContent() {
00200         global $wgShowHostnames, $wgShowSQLErrors;
00201 
00202         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00203             return $this->getMessage();
00204         } else {
00205             return 'DB connection error';
00206         }
00207     }
00208 
00214     public function reportHTML() {
00215         global $wgUseFileCache;
00216 
00217         // Check whether we can serve a file-cached copy of the page with the error underneath
00218         if ( $wgUseFileCache ) {
00219             try {
00220                 $cache = $this->fileCachedPage();
00221                 // Cached version on file system?
00222                 if ( $cache !== null ) {
00223                     // Hack: extend the body for error messages
00224                     $cache = str_replace( array( '</html>', '</body>' ), '', $cache );
00225                     // Add cache notice...
00226                     $cache .= '<div style="border:1px solid #ffd0d0;padding:1em;">' .
00227                         htmlspecialchars( $this->msg( 'dberr-cachederror',
00228                             'This is a cached copy of the requested page, and may not be up to date.' ) ) .
00229                         '</div>';
00230 
00231                     // Output cached page with notices on bottom and re-close body
00232                     echo "{$cache}<hr />{$this->getHTML()}</body></html>";
00233 
00234                     return;
00235                 }
00236             } catch ( MWException $e ) {
00237                 // Do nothing, just use the default page
00238             }
00239         }
00240 
00241         // We can't, cough and die in the usual fashion
00242         parent::reportHTML();
00243     }
00244 
00248     function searchForm() {
00249         global $wgSitename, $wgCanonicalServer, $wgRequest;
00250 
00251         $usegoogle = htmlspecialchars( $this->msg(
00252             'dberr-usegoogle',
00253             'You can try searching via Google in the meantime.'
00254         ) );
00255         $outofdate = htmlspecialchars( $this->msg(
00256             'dberr-outofdate',
00257             'Note that their indexes of our content may be out of date.'
00258         ) );
00259         $googlesearch = htmlspecialchars( $this->msg( 'searchbutton', 'Search' ) );
00260 
00261         $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
00262 
00263         $server = htmlspecialchars( $wgCanonicalServer );
00264         $sitename = htmlspecialchars( $wgSitename );
00265 
00266         $trygoogle = <<<EOT
00267 <div style="margin: 1.5em">$usegoogle<br />
00268 <small>$outofdate</small>
00269 </div>
00270 <form method="get" action="//www.google.com/search" id="googlesearch">
00271     <input type="hidden" name="domains" value="$server" />
00272     <input type="hidden" name="num" value="50" />
00273     <input type="hidden" name="ie" value="UTF-8" />
00274     <input type="hidden" name="oe" value="UTF-8" />
00275 
00276     <input type="text" name="q" size="31" maxlength="255" value="$search" />
00277     <input type="submit" name="btnG" value="$googlesearch" />
00278     <p>
00279         <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
00280         <label><input type="radio" name="sitesearch" value="" />WWW</label>
00281     </p>
00282 </form>
00283 EOT;
00284 
00285         return $trygoogle;
00286     }
00287 
00291     private function fileCachedPage() {
00292         $context = RequestContext::getMain();
00293 
00294         if ( $context->getOutput()->isDisabled() ) {
00295             // Done already?
00296             return '';
00297         }
00298 
00299         if ( $context->getTitle() ) {
00300             // Use the main context's title if we managed to set it
00301             $t = $context->getTitle()->getPrefixedDBkey();
00302         } else {
00303             // Fallback to the raw title URL param. We can't use the Title
00304             // class is it may hit the interwiki table and give a DB error.
00305             // We may get a cache miss due to not sanitizing the title though.
00306             $t = str_replace( ' ', '_', $context->getRequest()->getVal( 'title' ) );
00307             if ( $t == '' ) { // fallback to main page
00308                 $t = Title::newFromText(
00309                     $this->msg( 'mainpage', 'Main Page' ) )->getPrefixedDBkey();
00310             }
00311         }
00312 
00313         $cache = HTMLFileCache::newFromTitle( $t, 'view' );
00314         if ( $cache->isCached() ) {
00315             return $cache->fetchText();
00316         } else {
00317             return '';
00318         }
00319     }
00320 }
00321 
00325 class DBQueryError extends DBExpectedError {
00326     public $error, $errno, $sql, $fname;
00327 
00335     function __construct( DatabaseBase $db, $error, $errno, $sql, $fname ) {
00336         $message = "A database error has occurred. Did you forget to run " .
00337             "maintenance/update.php after upgrading?  See: " .
00338             "https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
00339             "Query: $sql\n" .
00340             "Function: $fname\n" .
00341             "Error: $errno $error\n";
00342         parent::__construct( $db, $message );
00343 
00344         $this->error = $error;
00345         $this->errno = $errno;
00346         $this->sql = $sql;
00347         $this->fname = $fname;
00348     }
00349 
00353     function isLoggable() {
00354         // Don't send to the exception log, already in dberror log
00355         return false;
00356     }
00357 
00361     function getPageTitle() {
00362         return $this->msg( 'databaseerror', 'Database error' );
00363     }
00364 
00368     protected function getHTMLContent() {
00369         $key = 'databaseerror-text';
00370         $s = Html::element( 'p', array(), $this->msg( $key, $this->getFallbackMessage( $key ) ) );
00371 
00372         $details = $this->getTechnicalDetails();
00373         if ( $details ) {
00374             $s .= '<ul>';
00375             foreach ( $details as $key => $detail ) {
00376                 $s .= str_replace(
00377                     '$1', call_user_func_array( 'Html::element', $detail ),
00378                     Html::element( 'li', array(),
00379                         $this->msg( $key, $this->getFallbackMessage( $key ) )
00380                     )
00381                 );
00382             }
00383             $s .= '</ul>';
00384         }
00385 
00386         return $s;
00387     }
00388 
00392     protected function getTextContent() {
00393         $key = 'databaseerror-textcl';
00394         $s = $this->msg( $key, $this->getFallbackMessage( $key ) ) . "\n";
00395 
00396         foreach ( $this->getTechnicalDetails() as $key => $detail ) {
00397             $s .= $this->msg( $key, $this->getFallbackMessage( $key ), $detail[2] ) . "\n";
00398         }
00399 
00400         return $s;
00401     }
00402 
00416     protected function getTechnicalDetails() {
00417         global $wgShowHostnames, $wgShowSQLErrors;
00418 
00419         $attribs = array( 'dir' => 'ltr' );
00420         $details = array();
00421 
00422         if ( $wgShowSQLErrors ) {
00423             $details['databaseerror-query'] = array(
00424                 'div', array( 'class' => 'mw-code' ) + $attribs, $this->sql );
00425         }
00426 
00427         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00428             $errorMessage = $this->errno . ' ' . $this->error;
00429             $details['databaseerror-function'] = array( 'code', $attribs, $this->fname );
00430             $details['databaseerror-error'] = array( 'samp', $attribs, $errorMessage );
00431         }
00432 
00433         return $details;
00434     }
00435 
00440     private function getFallbackMessage( $key ) {
00441         $messages = array(
00442             'databaseerror-text' => 'A database query error has occurred.
00443 This may indicate a bug in the software.',
00444             'databaseerror-textcl' => 'A database query error has occurred.',
00445             'databaseerror-query' => 'Query: $1',
00446             'databaseerror-function' => 'Function: $1',
00447             'databaseerror-error' => 'Error: $1',
00448         );
00449 
00450         return $messages[$key];
00451     }
00452 }
00453 
00457 class DBUnexpectedError extends DBError {
00458 }