前提条件
- PHP内部の文字コードはUTF-8
- MySQLのversioinが5.1
- ストレージエンジンがMyISAM
- 今回は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したら、すべて同じ値で上書きされたことがありましたので、ご注意ください。