Help us understand the problem. What is going on with this article?

PDOのプレースホルダーの有効性の検証

More than 1 year has passed since last update.

前提条件

  1. PHP内部の文字コードはUTF-8
  2. MySQLのversioinが5.1
  3. ストレージエンジンがMyISAM
  4. 今回はVARCHAR、INT、BLOBで検証

テーブルがこんな感じです

mysql
CREATE TABLE IF NOT EXISTS TEST (
    TESTID      INT(11)       PRIMARY KEY NOT NULL,
    TESTNAME    VARCHAR(255)  DEFAULT NULL,
    TESTNUMBER  INT(11)       DEFAULT 0,
    TESTBLOB    BLOB          DEFAULT NULL,
    DELFLG      INT(1)        DEFAULT 0     NOT NULL,
    UPDDATE     DATETIME      NOT NULL,
    INPDATE     TIMESTAMP     DEFAULT CURRENT_TIMESTAMP NOT NULL
);

とりあえずいろんな記号を入れてみる

日本語、半角カナ、記号、機種依存文字それぞれ入れてみました。
まずはインサート

insert.php
    $sql = <<<SQL
        INSERT INTO TEST (
            TESTID,
            TESTNAME,
            TESTNUMBER,
            TESTBLOB,
            DELFLG, UPDDATE, INPDATE
        ) SELECT
            (SELECT IFNULL(MAX(TESTID), 0)+1 FROM TEST),
            :TESTNAME,
            :TESTNUMBER,
            :TESTBLOB,
            0, NOW(), NOW()
        FROM DUAL;

SQL;

    $TESTNAME   = "てすとテスト!\"#$%&'()=~|`{+*}<>?_-^\@[;:],./①";
    $TESTNUMBER = "てすとテスト!\"#$%&'()=~|`{+*}<>?_-^\@[;:],./①";
    $TESTBLOB   = "てすとテスト!\"#$%&'()=~|`{+*}<>?_-^\@[;:],./①";

    $stmt = $GLOBALS['MYDB']->prepare($sql);

    $stmt->bindParam(':TESTNAME',    $TESTNAME,    PDO::PARAM_STR);
    $stmt->bindParam(':TESTNUMBER',  $TESTNUMBER,  PDO::PARAM_INT);
    $stmt->bindParam(':TESTBLOB',    $TESTBLOB,    PDO::PARAM_LOB);
    $stmt->execute();

selectしてデータを確認してみます。

    $sql = <<<SQL
        SELECT 
        TESTID, TESTNAME, TESTNUMBER,TESTBLOB
        FROM TEST 
        ORDER BY TESTID DESC LIMIT 1;
SQL;

    $stmt = $GLOBALS['MYDB']->prepare($sql);
    $stmt->execute();
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $DB_TESTNAME    = $row['TESTNAME'];
        $DB_TESTNUMBER  = $row['TESTNUMBER'];
        $DB_TESTBLOB    = $row['TESTBLOB'];
    }

    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $type  = $finfo->buffer($DB_TESTBLOB);

    $txt = <<<TXT
<pre>
DB_TESTNAME:{$DB_TESTNAME}
DB_TESTNUMBER:{$DB_TESTNUMBER}
</pre>
TXT;
    echo $txt;
    echo '<img src="data:'. $type .';base64, ' . base64_encode($DB_TESTBLOB) . '" >';

結果がこちら

DB_TESTNAME:てすとテスト!"#$%&'()=~|`{+*}<>?_-^\@[;:],./①
DB_TESTNUMBER:0   
<img src="data:text/plain;base64, 44Gm44GZ44Go776D7729776EISIjJCUmJygpPX58YHsrKn08Pj9fLV5cQFs7Ol0sLi/ikaA=">

INTとBLOBに無理やり突っ込みましたが、エラーを吐かずに処理してくれました。
画像はURLで開くと文字化けしたものが出てきました。
VARCHARに関しては機種依存文字でもきちんと文字としてinsertしてくれました。

制御文字コードを入れてみる

制御文字コードの間に日本語を入れて確認してみます。

\0い\aう\bえ\tお\nか\vき\fく\rけ\eこ\r\nさ

結果がこちら

水平タブ、改行コードは有効で、null文字はきちんと削除されました。
画像はURLを開くとダウンロードが始まり、中身を見ると上記で表示されたような文字になっていました。

文字コード 名前/意味 結果
\0 Null文字 削除
\a ベル そのまま
\b 後退 そのまま
\t 水平タブ 有効
\n 改行 有効
\v 垂直タブ 削除
\f 書式送り 削除
\r 復帰 有効
\e エスケープ そのまま
\r\n 改行 有効

文字化けを入れてみる

    $str = mb_convert_encoding("あいうえお", 'SJIS', 'UTF-8');

    echo $str;    //����������

    $TESTNAME   = $str;
    $TESTNUMBER = $str;
    $TESTBLOB   = $str;
    $stmt = $GLOBALS['MYDB']->prepare($sql);

    $stmt->bindParam(':TESTNAME',     $TESTNAME,     PDO::PARAM_STR);
    $stmt->bindParam(':TESTNUMBER',   $TESTNUMBER,     PDO::PARAM_INT);
    $stmt->bindParam(':TESTBLOB',     $TESTBLOB,     PDO::PARAM_LOB);
    $stmt->execute();

結果がこちら

DB_TESTNAME:
DB_TESTNUMBER:0
<img src="data:text/plain;base64, gqCCooKkgqaCqA==" >

文字化けは一文字も入らずに、画像を開くと全く別の記号になっていました。

bindParamの第三引数(data_type)を指定せずに入れてみる

      $stmt = $GLOBALS['MYDB']->prepare($sql);

      $stmt->bindParam(':TESTNAME',   $TESTNAME);
      $stmt->bindParam(':TESTNUMBER', $TESTNUMBER);
      $stmt->bindParam(':TESTBLOB',   $TESTBLOB);

結論としては先ほどの2パターンの文字や記号、制御コードを入れた場合と結果は変わりませんでした。

まとめ

今回の検証で、プレースホルダはかなり有効な事が分かりました。
PHPでDBを扱う場合は必ず使ってください。
ちなみにヒアドキュメント等で記述した文字列の改行も1文字に含まれるので気を付けてください。
bindParamは参照渡し、bindValueは値渡しで基本的にはbindValueを使ってください。
以前bindParamを使ってupdateしたら、すべて同じ値で上書きされたことがありましたので、ご注意ください。

okdyy75
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away