MediaWiki  REL1_24
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         $args = array_slice( func_get_args(), 2 );
00137 
00138         if ( $this->useMessageCache() ) {
00139             return wfMessage( $key, $args )->useDatabase( false )->text();
00140         } else {
00141             return wfMsgReplaceArgs( $fallback, $args );
00142         }
00143     }
00144 
00148     function isLoggable() {
00149         // Don't send to the exception log, already in dberror log
00150         return false;
00151     }
00152 
00156     function getHTML() {
00157         global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
00158 
00159         $sorry = htmlspecialchars( $this->msg(
00160             'dberr-problems',
00161             'Sorry! This site is experiencing technical difficulties.'
00162         ) );
00163         $again = htmlspecialchars( $this->msg(
00164             'dberr-again',
00165             'Try waiting a few minutes and reloading.'
00166         ) );
00167 
00168         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00169             $info = str_replace(
00170                 '$1', Html::element( 'span', array( 'dir' => 'ltr' ), $this->error ),
00171                 htmlspecialchars( $this->msg( 'dberr-info', '(Cannot contact the database server: $1)' ) )
00172             );
00173         } else {
00174             $info = htmlspecialchars( $this->msg(
00175                 'dberr-info-hidden',
00176                 '(Cannot contact the database server)'
00177             ) );
00178         }
00179 
00180         # No database access
00181         MessageCache::singleton()->disable();
00182 
00183         $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
00184 
00185         if ( $wgShowDBErrorBacktrace ) {
00186             $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
00187         }
00188 
00189         $html .= '<hr />';
00190         $html .= $this->searchForm();
00191 
00192         return $html;
00193     }
00194 
00195     protected function getTextContent() {
00196         global $wgShowHostnames, $wgShowSQLErrors;
00197 
00198         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00199             return $this->getMessage();
00200         } else {
00201             return 'DB connection error';
00202         }
00203     }
00204 
00210     public function reportHTML() {
00211         global $wgUseFileCache;
00212 
00213         // Check whether we can serve a file-cached copy of the page with the error underneath
00214         if ( $wgUseFileCache ) {
00215             try {
00216                 $cache = $this->fileCachedPage();
00217                 // Cached version on file system?
00218                 if ( $cache !== null ) {
00219                     // Hack: extend the body for error messages
00220                     $cache = str_replace( array( '</html>', '</body>' ), '', $cache );
00221                     // Add cache notice...
00222                     $cache .= '<div style="border:1px solid #ffd0d0;padding:1em;">' .
00223                         htmlspecialchars( $this->msg( 'dberr-cachederror',
00224                             'This is a cached copy of the requested page, and may not be up to date.' ) ) .
00225                         '</div>';
00226 
00227                     // Output cached page with notices on bottom and re-close body
00228                     echo "{$cache}<hr />{$this->getHTML()}</body></html>";
00229 
00230                     return;
00231                 }
00232             } catch ( MWException $e ) {
00233                 // Do nothing, just use the default page
00234             }
00235         }
00236 
00237         // We can't, cough and die in the usual fashion
00238         parent::reportHTML();
00239     }
00240 
00244     function searchForm() {
00245         global $wgSitename, $wgCanonicalServer, $wgRequest;
00246 
00247         $usegoogle = htmlspecialchars( $this->msg(
00248             'dberr-usegoogle',
00249             'You can try searching via Google in the meantime.'
00250         ) );
00251         $outofdate = htmlspecialchars( $this->msg(
00252             'dberr-outofdate',
00253             'Note that their indexes of our content may be out of date.'
00254         ) );
00255         $googlesearch = htmlspecialchars( $this->msg( 'searchbutton', 'Search' ) );
00256 
00257         $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
00258 
00259         $server = htmlspecialchars( $wgCanonicalServer );
00260         $sitename = htmlspecialchars( $wgSitename );
00261 
00262         $trygoogle = <<<EOT
00263 <div style="margin: 1.5em">$usegoogle<br />
00264 <small>$outofdate</small>
00265 </div>
00266 <form method="get" action="//www.google.com/search" id="googlesearch">
00267     <input type="hidden" name="domains" value="$server" />
00268     <input type="hidden" name="num" value="50" />
00269     <input type="hidden" name="ie" value="UTF-8" />
00270     <input type="hidden" name="oe" value="UTF-8" />
00271 
00272     <input type="text" name="q" size="31" maxlength="255" value="$search" />
00273     <input type="submit" name="btnG" value="$googlesearch" />
00274     <p>
00275         <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
00276         <label><input type="radio" name="sitesearch" value="" />WWW</label>
00277     </p>
00278 </form>
00279 EOT;
00280 
00281         return $trygoogle;
00282     }
00283 
00287     private function fileCachedPage() {
00288         $context = RequestContext::getMain();
00289 
00290         if ( $context->getOutput()->isDisabled() ) {
00291             // Done already?
00292             return '';
00293         }
00294 
00295         if ( $context->getTitle() ) {
00296             // Use the main context's title if we managed to set it
00297             $t = $context->getTitle()->getPrefixedDBkey();
00298         } else {
00299             // Fallback to the raw title URL param. We can't use the Title
00300             // class is it may hit the interwiki table and give a DB error.
00301             // We may get a cache miss due to not sanitizing the title though.
00302             $t = str_replace( ' ', '_', $context->getRequest()->getVal( 'title' ) );
00303             if ( $t == '' ) { // fallback to main page
00304                 $t = Title::newFromText(
00305                     $this->msg( 'mainpage', 'Main Page' ) )->getPrefixedDBkey();
00306             }
00307         }
00308 
00309         $cache = new HTMLFileCache( $t, 'view' );
00310         if ( $cache->isCached() ) {
00311             return $cache->fetchText();
00312         } else {
00313             return '';
00314         }
00315     }
00316 }
00317 
00321 class DBQueryError extends DBExpectedError {
00322     public $error, $errno, $sql, $fname;
00323 
00331     function __construct( DatabaseBase $db, $error, $errno, $sql, $fname ) {
00332         $message = "A database error has occurred. Did you forget to run " .
00333             "maintenance/update.php after upgrading?  See: " .
00334             "https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
00335             "Query: $sql\n" .
00336             "Function: $fname\n" .
00337             "Error: $errno $error\n";
00338         parent::__construct( $db, $message );
00339 
00340         $this->error = $error;
00341         $this->errno = $errno;
00342         $this->sql = $sql;
00343         $this->fname = $fname;
00344     }
00345 
00349     function getPageTitle() {
00350         return $this->msg( 'databaseerror', 'Database error' );
00351     }
00352 
00356     protected function getHTMLContent() {
00357         $key = 'databaseerror-text';
00358         $s = Html::element( 'p', array(), $this->msg( $key, $this->getFallbackMessage( $key ) ) );
00359 
00360         $details = $this->getTechnicalDetails();
00361         if ( $details ) {
00362             $s .= '<ul>';
00363             foreach ( $details as $key => $detail ) {
00364                 $s .= str_replace(
00365                     '$1', call_user_func_array( 'Html::element', $detail ),
00366                     Html::element( 'li', array(),
00367                         $this->msg( $key, $this->getFallbackMessage( $key ) )
00368                     )
00369                 );
00370             }
00371             $s .= '</ul>';
00372         }
00373 
00374         return $s;
00375     }
00376 
00380     protected function getTextContent() {
00381         $key = 'databaseerror-textcl';
00382         $s = $this->msg( $key, $this->getFallbackMessage( $key ) ) . "\n";
00383 
00384         foreach ( $this->getTechnicalDetails() as $key => $detail ) {
00385             $s .= $this->msg( $key, $this->getFallbackMessage( $key ), $detail[2] ) . "\n";
00386         }
00387 
00388         return $s;
00389     }
00390 
00404     protected function getTechnicalDetails() {
00405         global $wgShowHostnames, $wgShowSQLErrors;
00406 
00407         $attribs = array( 'dir' => 'ltr' );
00408         $details = array();
00409 
00410         if ( $wgShowSQLErrors ) {
00411             $details['databaseerror-query'] = array(
00412                 'div', array( 'class' => 'mw-code' ) + $attribs, $this->sql );
00413         }
00414 
00415         if ( $wgShowHostnames || $wgShowSQLErrors ) {
00416             $errorMessage = $this->errno . ' ' . $this->error;
00417             $details['databaseerror-function'] = array( 'code', $attribs, $this->fname );
00418             $details['databaseerror-error'] = array( 'samp', $attribs, $errorMessage );
00419         }
00420 
00421         return $details;
00422     }
00423 
00428     private function getFallbackMessage( $key ) {
00429         $messages = array(
00430             'databaseerror-text' => 'A database query error has occurred.
00431 This may indicate a bug in the software.',
00432             'databaseerror-textcl' => 'A database query error has occurred.',
00433             'databaseerror-query' => 'Query: $1',
00434             'databaseerror-function' => 'Function: $1',
00435             'databaseerror-error' => 'Error: $1',
00436         );
00437 
00438         return $messages[$key];
00439     }
00440 }
00441 
00445 class DBUnexpectedError extends DBError {
00446 }