wordpress WordPress でのデータベースエラー時の表示

2013年6月10日

データベースエラー時の表示

  • WordPress は MySQL を使って動作するが、特に共有のレンタルサーバーでは MySQL が過負荷で短時間停止する場合がある。
  • このようなときに、db-error.php を追加しておくと時間を置いた後の再アクセスを閲覧者に促すことができる。
  • 但し、db-error.php はテンプレートファイルではないため、テンプレートタグは使えない。
  • CSS へのパスなども絶対パスで記述する必要がある。
  • また、テーマフォルダではなく、wp-content 直下に配置する。

以下は、db-error.php の例。

  • スタイルシートも wp-content 直下に配置して、絶対パスで読み込み。
  • スタイルシートには、タイトルロゴなどのスタイリングも記述。
  • 503 ステータス コードを返す。サーチエンジン対策:サービスが一時的に使用不可能になっていることを表す「503: Service Unavailable」というHTTPステータスコードで示す。
  • 「Retry-After: 600 」はクライアントがどのくらい後にリクエストを再試行すべきかを示すための記述(この場合5分)。
<?php
header( 'HTTP/1.1 503 Service Unavailable' );
header( 'Content-Type: text/html; charset=utf-8' );
header( 'Retry-After: 600' );
?><!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Mysite | DB Error</title>
<link rel="stylesheet" href="http://mysite.com/wp-content/db-error.css" type="text/css" /> 
</head>
<body>
<div id="container">
<div id="header">
<h1 id="toptitle"><a href="http://www.mysite.com/">Mysite</a></h1>
</div><!-- end of #header -->

<div id="content">
<h2>503 Service Unavailable - DB Error</h2>
    <p>ただいまシステム停止中です。大変ご迷惑をおかけしております。<br />
    数分後に再度アクセスお願いいたします。</p>
    <p>ご不便おかけしますが、よろしくお願いいたします。</p>
    <p><a href="http://mysite.com/">http://mysite.com/</a></p>

</div><!-- end of #content -->

<div id="footer">
<address>Copyright &copy; mysite. All rights reserved. </address>
</div><!-- end of #footer -->
</div><!-- end of #container -->
</body>
</html>

訂正:以下は不確かな内容です。もう少し調べてわかれば変更・修正する予定です。

ローカル環境と試験環境で試したが、作成したエラーメッセージは表示されず以下のメッセージが表示される。(試し方が、まずい可能性がある。試した方法は、データベース名やパスワードを変更するというもので、これではまず接続確立のエラーになるのは当然か。。。)

データベース接続確立のエラー
これは wp-config.php ファイルのユーザー名とパスワードが間違っているか、 localhost のデータベースサーバーに接続できないかのどちらかを意味します。ホストのデータベースサーバーがダウンしているのかもしれません。
•ユーザー名とパスワードに間違いはありませんか ?
•ホスト名に間違いはありませんか ?
•データベースサーバーは動いていますか ?

追記:先日、ローカル環境をサーバーに移行する際に、うまくいかず「データベース接続エラー」という表示になった。データベース名が違っている場合は上記のエラーがでたので、この違いは調べる必要がある。でも作成した「db-error.php 」は表示されなかった。(???)

調べてみると「dead_db()」という関数が「db-error.php」ファイルがあれば表示するようになっている。

//wp-includes/functions.php
function dead_db() {
  global $wpdb;

  // Load custom DB error template, if present. ここ
  if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
    require_once( WP_CONTENT_DIR . '/db-error.php' );
    die();
  }

  // If installing or in the admin, provide the verbose message.
  if ( defined('WP_INSTALLING') || defined('WP_ADMIN') )
    wp_die($wpdb->error);

  // Otherwise, be terse.
  status_header( 500 );
  nocache_headers();
  header( 'Content-Type: text/html; charset=utf-8' );

  wp_load_translations_early();
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"<?php if ( is_rtl() ) echo ' dir="rtl"'; ?>>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title><?php _e( 'Database Error' ); ?></title>

</head>
<body>
  <h1><?php _e( 'Error establishing a database connection' ); ?></h1>
</body>
</html>
<?php
  die();
}
&#91;/code&#93;

「dead_db()」は、以下の2箇所で呼び出されていて、データベース関連のエラーがあるときに実行される。

/wp-includes/load.php -> line 350

function wp_set_wpdb_vars() { global $wpdb, $table_prefix; if ( !empty( $wpdb->error ) ) dead_db(); $wpdb->field_types = array( 'post_author' => '%d', 'post_parent' => '%d', 'menu_order' => '%d', 'term_id' => '%d', 'term_group' => '%d', 'term_taxonomy_id' => '%d', 'parent' => '%d', 'count' => '%d','object_id' => '%d', 'term_order' => '%d', 'ID' => '%d', 'comment_ID' => '%d', 'comment_post_ID' => '%d', 'comment_parent' => '%d', 'user_id' => '%d', 'link_id' => '%d', 'link_owner' => '%d', 'link_rating' => '%d', 'option_id' => '%d', 'blog_id' => '%d', 'meta_id' => '%d', 'post_id' => '%d', 'user_status' => '%d', 'umeta_id' => '%d', 'comment_karma' => '%d', 'comment_count' => '%d', // multisite: 'active' => '%d', 'cat_id' => '%d', 'deleted' => '%d', 'lang_id' => '%d', 'mature' => '%d', 'public' => '%d', 'site_id' => '%d', 'spam' => '%d', ); $prefix = $wpdb->set_prefix( $table_prefix ); if ( is_wp_error( $prefix ) ) { wp_load_translations_early(); wp_die( __( '<strong>ERROR</strong>: <code>$table_prefix</code> in <code>wp-config.php</code> can only contain numbers, letters, and underscores.' ) ); } }

/wp-includes/functions.php -> line 1154

function is_blog_installed() {
  global $wpdb;

  // Check cache first. If options table goes away and we have true cached, oh well.
  if ( wp_cache_get( 'is_blog_installed' ) )
    return true;

  $suppress = $wpdb->suppress_errors();
  if ( ! defined( 'WP_INSTALLING' ) ) {
    $alloptions = wp_load_alloptions();
  }
  ・・・省略・・・
    // One or more tables exist. We are insane.

    wp_load_translations_early();

    // Die with a DB error.
    $wpdb->error = sprintf( __( 'One or more database tables are unavailable. The database may need to be <a href="%s">repaired</a>.' ), 'maint/repair.php?referrer=is_blog_installed' );
    dead_db();
  }

  $wpdb->suppress_errors( $suppress );

  wp_cache_set( 'is_blog_installed', false );

  return false;
}

wp_set_wpdb_vars() は /wp-settings.php -> line 76 で呼び出されている。

<?php
/**
 * Used to set up and fix common variables and include
 * the WordPress procedural and class library.
 * Allows for some configuration in wp-config.php (see default-constants.php)
 */

/**
 * Stores the location of the WordPress directory of functions, classes, and core content.
 */
define( 'WPINC', 'wp-includes' );

// Include files required for initialization.
require( ABSPATH . WPINC . '/load.php' );
require( ABSPATH . WPINC . '/default-constants.php' );
require( ABSPATH . WPINC . '/version.php' );

// Set initial default constants including WP_MEMORY_LIMIT, WP_MAX_MEMORY_LIMIT, WP_DEBUG, WP_CONTENT_DIR and WP_CACHE.
wp_initial_constants( );

・・・省略・・・

// Load early WordPress files.
require( ABSPATH . WPINC . '/compat.php' );
require( ABSPATH . WPINC . '/functions.php' );
require( ABSPATH . WPINC . '/class-wp.php' );
require( ABSPATH . WPINC . '/class-wp-error.php' );
require( ABSPATH . WPINC . '/plugin.php' );
require( ABSPATH . WPINC . '/pomo/mo.php' );

// Include the wpdb class and, if present, a db.php database drop-in.
require_wp_db();

// Set the database table prefix and the format specifiers for database table columns.
$GLOBALS&#91;'table_prefix'&#93; = $table_prefix;
wp_set_wpdb_vars();

// Start the WordPress object cache, or an external object cache if the drop-in is present.
wp_start_object_cache();
&#91;/code&#93;

それでは、「ユーザー名とパスワードに間違いはありませんか ?・・・」などのメッセージはどこから来ているのか?

言語ファイル(ja.po)で検索して、その英語の表現を調べると該当する部分がある。

&#91;code&#93;
\n
<h1>Error establishing a database connection</h1>\n
<p>This either means that the username and password information in your <code>wp-config.php</code> file is incorrect or we can't contact the database server at <code>%s</code>. This could mean your host's database server is down.</p>\n
<ul>\n
\t<li>Are you sure you have the correct username and password?</li>\n
\t<li>Are you sure that you have typed the correct hostname?</li>\n
\t<li>Are you sure that the database server is running?</li>\n
</ul>\n
<p>If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href='http://wordpress.org/support/'>WordPress Support Forums</a>.</p>\n

上記の文で検索すると、/wp-includes/wp-db.php -> line 1125 で定義されていて、/wp-includes/wp-db.php -> line 549 で呼び出されている。

WordPress DB Class のコンストラクタ

//wp-includes/wp-db.php -> line 549
function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
    register_shutdown_function( array( $this, '__destruct' ) );

    if ( WP_DEBUG )
      $this->show_errors();

    $this->init_charset();

    $this->dbuser = $dbuser;
    $this->dbpassword = $dbpassword;
    $this->dbname = $dbname;
    $this->dbhost = $dbhost;

    $this->db_connect();
  }

データベースの選択と接続

//wp-includes/wp-db.php -> line 1125
function db_connect() {

    $this->is_mysql = true;

    $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
    $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;

    if ( WP_DEBUG ) {
      $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    } else {
      $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    }

    if ( !$this->dbh ) {
      wp_load_translations_early();
      $this->bail( sprintf( __( "
<h1>Error establishing a database connection</h1>
<p>This either means that the username and password information in your <code>wp-config.php</code> file is incorrect or we can't contact the database server at <code>%s</code>. This could mean your host's database server is down.</p>
<ul>
  <li>Are you sure you have the correct username and password?</li>
  <li>Are you sure that you have typed the correct hostname?</li>
  <li>Are you sure that the database server is running?</li>
</ul>
<p>If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href='http://wordpress.org/support/'>WordPress Support Forums</a>.</p>
" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' );

      return;
    }

    $this->set_charset( $this->dbh );

    $this->ready = true;

    $this->select( $this->dbname, $this->dbh );
  }

「bail」というメソッドを使って例の文を出力しているので、「bail」を調べると /wp-includes/wp-db.php -> line 1617 で次のように定義されている。

function bail( $message, $error_code = '500' ) {
    if ( !$this->show_errors ) {
      if ( class_exists( 'WP_Error' ) )
        $this->error = new WP_Error($error_code, $message);
      else
        $this->error = $message;
      return false;
    }
    wp_die($message);
  }

データベース接続時に問題があると、「bail」というメソッドを使って「wp_die($message)」で例の文を出力している。

//wp-includes/functions.php -> line 2037
function wp_die( $message = '', $title = '', $args = array() ) {
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
    $function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
  elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
    $function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
  else
    $function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );

  call_user_func( $function, $message, $title, $args );
}

取りあえず、db-error.php のエラーメッセージを表示するには、wp-db.php の $this->bail の実行の前に dead_db(); を実行させれば、表示される。以下のように wp-db.php に dead_db(); を追加して、ローカル環境で試してみると、エラーメッセージが表示されることは確認できたが、果たしてこれで問題がないかは不明。というより、やはりコアの部分なのでできればいじりたくない部分。

また、この時点では WordPress の関数などは読み込まれていないので、「is_user_logged_in() 」の関数やテンプレートタグなどは使用できない。

//wp-includes/wp-db.php -> line 1125
function db_connect() {

    $this->is_mysql = true;

    $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
    $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;

    if ( WP_DEBUG ) {
      $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    } else {
      $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    }

    if ( !$this->dbh ) {
      wp_load_translations_early();

      dead_db();  //追記した部分。

      $this->bail( sprintf( __( "
<h1>Error establishing a database connection</h1>
<p>This either means that the username and password information in your <code>wp-config.php</code> file is incorrect or we can't contact the database server at <code>%s</code>. This could mean your host's database server is down.</p>
<ul>
  <li>Are you sure you have the correct username and password?</li>
  <li>Are you sure that you have typed the correct hostname?</li>
  <li>Are you sure that the database server is running?</li>
</ul>
<p>If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href='http://wordpress.org/support/'>WordPress Support Forums</a>.</p>
" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' );

      return;
    }

    $this->set_charset( $this->dbh );

    $this->ready = true;

    $this->select( $this->dbname, $this->dbh );
  }

どのタイミングで「dead_db()」が呼ばれるのかは、さらに調べる必要がありそう。。。
wp-settings.php が読み込まれるのと、db_connect() が実行されるのとどちらが先なのか?おそらく、db_connect() が先なのだと思うが、今のところ不明。