PHP
MySQL
PDO
プログラミング初心者

[PHP+MySQL]DSNの駄目なスペースはどれか

はじめに

開発・サーバー保守メインで、補助的にプログラミング学習の質問対応も担当しているのですが、

  • 「DBに接続出来ているのにデータが入りません!」
  • 「接続でエラーは出てないんですけど…」
  • 「var_dumpで見たらPDOのオブジェクトはできていたんですが…」

などの相談を受けて、

  • 接続エラーになっていないが結果が得られていない。
  • DSNのスペースを詰めると解消する。

という現象にしばしば遭遇します。
毎回「DSN情報にスペースがあると正しく動かない時があるので、スペースを無くしてみてください。」と曖昧に答えることになるので、スペースがあるとエラーにならないが正常に動作しないのは具体的にどんな時なのかを調べてみました。

テスト用プログラム

dsn_test.php
$user= "hoge";
$pass= "huga";
$dsn = "mysql:dbname=DBNAME;host=localhost;charset=utf8mb4";//パターンは後述
header('Content-type: text/plain; charset=utf-8');
try{
    $pdo = new PDO($dsn,$user,$pass);
}catch (PDOException $e) {
    echo '接続失敗: ' . $e->getMessage()."\n";
}
echo "----PDO var_dump結果----\n";
var_dump($pdo);
echo "----SHOW TABLESしてとりあえず1項目をvar_dump----\n";
$stmt = $pdo->query('SHOW TABLES');
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($result[0]);

PHP 7.1 DSNの内容と挙動

PHP7.1でDSN部分の試したもの一覧
$dsn = "mysql:dbname=DBNAME;host=localhost;charset=utf8mb4";
// 正常に接続・結果取得できる。
$dsn = "mysql:dbname=DBNAME; host=localhost;charset=utf8mb4";
// 正常に接続・結果取得できる。
$dsn = "mysql:dbname=DBNAME ;host=localhost;charset=utf8mb4";
// エラー。DB名にスペースが入ってしまい、接続に失敗する。
$dsn = "mysql:dbname= DBNAME;host=localhost;charset=utf8mb4";
// エラー。DB名にスペースが入ってしまい、接続に失敗する。
$dsn = "mysql:dbname =DBNAME;host=localhost;charset=utf8mb4";
// ※ 接続段階ではエラーにならず、PDOインスタンスは存在するが、動作しない。
// Fatal error: Uncaught Error: Call to a member function fetchAll() on boolean
// 接続後$pdo->exec('USE `DBNAME`');でデータベースを指定すると正常に動作する。
$dsn = "mysql:dbname=DBNAME;host =localhost;charset=utf8mb4";
// ※ 正常に接続・結果取得できる。
$dsn = "mysql:dbname=DBNAME;host= localhost;charset=utf8mb4";
// エラーになる。ホスト名が不適切。
$dsn = "mysql:dbname=DBNAME;host=localhost ";
// エラーになる。ホスト名が不適切。
$dsn = "mysql:dbname=DBNAME;host=localhost; charset=utf8mb4";
// 接続・結果取得はできる。他の挙動から想像するとcharsetも正常と思われる。
$dsn = "mysql:dbname=DBNAME;host=localhost;charset =utf8mb4";
// 接続・結果取得はできる。charsetが正しく認識されているのかは怪しい?
$dsn = "mysql:dbname=DBNAME;host=localhost;charset= utf8mb4";
// エラーになる。charsetが不適切。
$dsn = "mysql:dbname=DBNAME;host=localhost;charset=utf8mb4 ";
// エラーになる。charsetが不適切。

$dsn = "mysql:dbname=DBNAME; host=localhost; charset=utf8mb4";
// 意外にも接続・結果取得できる。

PHP 5.2 DSNの内容と挙動

PHP5.2DSN部分の試したもの一覧
$dsn = "mysql:dbname=DATABASENAME;host=localhost";
// 正常に接続できる。
$dsn = "mysql:dbname=DATABASENAME; host=localhost";
// 正常に接続できる。
$dsn = "mysql:dbname=DATABASENAME ;host=localhost";
// エラー。DB名不適切で接続に失敗する。
$dsn = "mysql:dbname= DATABASENAME;host=localhost";
// エラー。DB名不適切で接続に失敗する。
$dsn = "mysql:dbname =DATABASENAME;host=localhost";
// 接続段階ではエラーにならず、PDOインスタンスは存在するが、動作しない。
$dsn = "mysql:dbname=DATABASENAME;host =localhost";
// 正常に接続できる。
$dsn = "mysql:dbname=DATABASENAME;host= localhost";
// エラー。ホスト名不適切で接続に失敗する。
$dsn = "mysql:dbname=DATABASENAME;host=localhost ";
// エラー。ホスト名不適切で接続に失敗する。

結果

  • 「イコール=より後、セミコロン;または末尾まで」は、引用符で囲った文字列のように、空白も含めてその項目の内容扱いされる。このため、この区間にスペースがあると、内容の文字列自体が間違っている状態になりエラーになる。
  • 「セミコロンから、次の項目名が始まるまで」の間にスペースがあるのは問題ない。やらないでほしいが全角スペースでも動く。
  • dbnameと直後のイコール=の間」にスペースがある場合は、接続に成功するが操作できない。これはhostやcharsetの時は起きない。
    • 理由はデータベース選択部分が丸ごと無視されていて、dbname=DBNAME;を省略した場合と同様の状態になり、データベース名を指定せずにDBサーバーに接続しているため。
      • プログラムからDB未指定で接続する機会が無かったので、これに思い当たりませんでした。

じゃあ、どう返答しようか?

  • DSN情報の=の後はスペースも含めて内容の一部になるので、スペースが混ざったホストやDBになってしまい、エラーになります。また、dbname直後の=だけは、前にスペースがあるとデータベースサーバーへの接続自体は成功するものの、実はデータベース名の指定が無視されt(略)
  • 「dbnameはDBサーバー内のどのDBを使うかの指定ですが、ここの=の前にスペースがあると無視されてしまうので、選んでいるDBの操作として書かれた一般的なSQL文では操作に失敗します。」
  • 「DSN情報の=の前後はスペースを入れないようにしましょう。詳しく書くと長いので割愛しますが、場所によっては上手く動きません。」
    • とりあえず質問者の理解度合いを見て「スペースを入れない」だけにして詳しい回答を求められたら答えるか、「失敗しているのはデータベース選択である」ことまで含むか使い分けることにします。

関連記事

関連リンク