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

### 前提条件
1. PHP内部の文字コードはUTF-8</li>
1. MySQLのversioinが5.1</li>
1. ストレージエンジンがMyISAM</li>
1. 今回はVARCHAR、INT、BLOBで検証</li>


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

```sql: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
);
```

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

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

```php: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してデータを確認してみます。

```php
    $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) . '" >';

```

結果がこちら

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

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

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

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

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

結果がこちら

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


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


### 文字化けを入れてみる

```php
    $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();
```

結果がこちら

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

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

### bindParamの第三引数(data_type)を指定せずに入れてみる
```php
      $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したら、すべて同じ値で上書きされたことがありましたので、ご注意ください。