10
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PHP で MySQL 接続時に必要な知識(最小限版)

Last updated at Posted at 2019-02-02

はじめに

mpyw さんの PHPでデータベースに接続するときのまとめ - Qiita という記事をいつも愛読しているのですが、初心者向けにはハードルが高い気がするので、接続に必要な最小限の内容にまとめてみました。
最終的には**【php のヤッカイなクセ】**も含めて理解する必要がありますが、学習当初は以下のテンプレートを使用することで、最低限守らなければならないルールは満たせます。

テンプレート

コードは、mpyw さんの記事を元に微調整しています。

コピペ用テンプレート
try {
    $pdo = new PDO(
        'mysql:dbname=testdb;host=localhost;charset=utf8',//or charset=utf8mb4
        'user',
        'password',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
//            PDO::MYSQL_ATTR_MULTI_STATEMENTS => false,
        ]
    );
    $stmt = $pdo->prepare('SELECT * FROM users WHERE age = :age AND gender = :gender');
    $stmt->bindValue(':age', (int)$age, PDO::PARAM_INT);
    $stmt->bindValue(':gender', $gender);
    $stmt->execute();
    $result = $stmt->fetchAll();
} catch (PDOException $e) {
    header('Content-Type: text/plain; charset=UTF-8', true, 500);
    exit($e->getMessage()); 
}
var_dump($result);

記述の前提

接続には PDO を使用します。
また、PDO::ATTR_EMULATE_PREPARESfalse、使用するcharsetutf8を想定しています。
(utf8mb4を利用するケースも多いです)

定石

PDO::ATTR_ERRMODE

SQL実行でエラーが起こった際、例外を投げる PDO::ERRMODE_EXCEPTION を選択します。このモードでは、エラーが発生した時点で スクリプトの実行を停止させ、catch で、問題発生箇所特定のための情報を表示します。
*実用時は、catchの内部はもっと考慮して記述する必要があります。

PDO::ATTR_DEFAULT_FETCH_MODE

結果セットにカラム名で添字を付けた配列を返す PDO::FETCH_ASSOC を選択します。他のモードも便利なので、理解が進めば試してください。
参考:PDOフェッチパターン大全

PDO::ATTR_EMULATE_PREPARES

エミュレートを falseにするとは、いわゆる静的プレースホルダを使用することを意味します。trueにすると動的プレースホルダです。
現行の php ではtrueが default ですが、過去のバージョンではfalseが default だった時期があります。挙動に差があるので明記しておくのが適切です。
動的プレースホルダはクセがあるので、当面静的プレースホルダ(false)を使用してください。
動的プレースホルダを使用する場合、複文を利用できないようにMYSQL_ATTR_MULTI_STATEMENTS => falseしておきましょう。
(使用できるバージョンが限定されるので、一応コメントにしています。7 系ならコメントを外してしまって良いです。)

参考1:PDOに複文実行を禁止するオプションが追加されていた-徳丸浩の日記

また、PDO::ATTR_EMULATE_PREPARESの【現時点での】セキュリティ的な意味合いに関して質問したところ、徳丸さんに回答いただけたので紹介しておきます。

参考2:PDO::ATTR_EMULATE_PREPARES => false は必要か?

プリペアドステートメントを使おう

SQL 文に、直接変数を埋め込むのは、SQL インジェクションを引き起こす原因となります。

NGパターン
$pdo->query('SELECT * FROM users WHERE age = ' . $age . ' AND gender = ' . $gender);

PDO::prepare() で、プリペアドステートメントを用意し、変数をバインドする方式を覚えてください。
バインドされる値に対して、引用符は自動付加されるので、PDO::prepare()内に引用符は記述しないことに注意してください。

bindValue を使おう

プレースホルダに値をバインドするメソッドには、PDOStatement::bindValuePDOStatement::bindParamがありますが、bindParamは SQL 実行時に値がバインドされるため、直感的に分かりにくいです。(あと、副作用があったり^^;)慣れないうちは、bindValueを使用しましょう。

#おまけ

PDO::PARAM_INT は罠 1

PDO::PARAM_INT は、以下のような実装になっているようです。
PDOでの数値列の扱いにはワナがいっぱい(2)

PDO::PARAM_INTはboolをintに変換(他は何もしない)

つまり、(string)'15' は、15 にならないのです。
この場合、'15' を渡すのはそもそもケシカラン!って考え方もありますが、テンプレートでは (int) でキャストする方法を記述しています。

LIKE も気をつけよう

MySQL であれば、LIKE はワイルドカードとして_%が使用されます。
(ワイルドカード使用時にエスケープに使う文字のデフォルトは、データベースエンジンによって違うので注意!エスケープのための文字の指定もできるので興味があれば調べてみてください。)
単純にバインドすると100%等の文字列の検索が正しく行われません。
ワイルドカードをエスケープしてやる必要があります。

MySQLの場合
$stmt = $pdo->prepare('SELECT * FROM users WHERE name LIKE ?');
$stmt->bindValue(1, '%' . addcslashes($name, '\_%') . '%', PDO::PARAM_STR);

まとめ

慣れれば大した事ではないし、そのうちフレームワークの機能で DB を扱うようになるので、あまり役に立つ情報ではないのですが、初心者が躓くポイントなのでまとめました。

最初に書いたとおり「最小限」の記述なので、最終的には元記事をよく読んで理解すると良いです。

  1. PHP 7.2 以降のエミュレーション ON の場合、int へキャストされます。詳しくはあの PDO::PARAM_INT の挙動が変更になってる!を参照してください。

10
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?