[PHP] `PDO`

なんか1年くらい前のメモが出てきた。以下、そのメモ。かなり未完成な状態の文章

参考にしそうなリンクたち

データベースへの接続と切断

<?php
try
{
  // MySQLサーバへ接続
  $pdo = new PDO
  (
    "mysql:host=localhost; dbname=fernwelt",
    "root",
    "password"
  );
  var_dump($pdo);
}
catch(PDOException $e)
{
  echo $e;
}
// 切断
$pdo = null;

結果

object(PDO)#1 (0) {
}

参考サイトのコードのまんま。PDO クラスのコンストラクタにより接続に成功すると PDO オブジェクトが返される。 PDO オブジェクトをダンプしても何も出てこない。

PDO のインスタンスを生成することでサーバに接続できる。コンストラクタの引数は、DSN (DataSourceName、データソース名)・ユーザ名・パスワード[・オプション]。オプションは上のソースでは省略されているけど、ドライバ固有の接続オプションを指定する引数。コンストラクタの詳細はPHP: PDO::__construct - Manualを見てね。

切断するには PDO インスタンスを消してしまえばいい。

接続エラー時には PDOException が投げられる。この例外は PDO のみが出すエラーで RuntimeException を継承した定義済みの例外。

結果

exception 'PDOException' with message 'SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: YES)' in C:\xampp\htdocs\index.php:10
Stack trace:
#0 C:\xampp\htdocs\index.php(11): PDO->__construct('mysql:host=loca...', 'root', 'password')
#1 {main}

object(PDOException)#2 (7) {
  ["message:protected"]=>
  string(86) "SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: YES)"
  ["string:private"]=>
  string(305) "exception 'PDOException' with message 'SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: YES)' in C:\xampp\htdocs\index.php:10
Stack trace:
#0 C:\xampp\htdocs\index.php(10): PDO->__construct('mysql:host=loca...', 'root', 'password')
#1 {main}"
  ["code:protected"]=>
  int(0)
  ["file:protected"]=>
  string(20) "C:\xampp\htdocs\index.php"
  ["line:protected"]=>
  int(10)
  ["trace:private"]=>
  array(1) {
    [0]=>
    array(6) {
      ["file"]=>
      string(20) "C:\xampp\htdocs\index.php"
      ["line"]=>
      int(10)
      ["function"]=>
      string(11) "__construct"
      ["class"]=>
      string(3) "PDO"
      ["type"]=>
      string(2) "->"
      ["args"]=>
      array(3) {
        [0]=>
        string(42) "mysql:host=localhost; dbname=fernwelt"
        [1]=>
        string(4) "root"
        [2]=>
        string(13) "password"
      }
    }
  }
  ["errorInfo"]=>
  NULL
}

アクセスが拒否されちゃったよ…とエラーが出る。Exception のものとは違う errorInfo プロパティは何だろう?

つーか PDO の中身って何さ。。

<?php
print_r(get_class_vars("PDO"));
print_r(get_class_methods("PDO"));

結果

Array
(
)
Array
(
    [0] => __construct
    [1] => prepare
    [2] => beginTransaction
    [3] => commit
    [4] => rollBack
    [5] => setAttribute
    [6] => exec
    [7] => query
    [8] => lastInsertId
    [9] => errorCode
    [10] => errorInfo
    [11] => getAttribute
    [12] => quote
    [13] => __wakeup
    [14] => __sleep
    [15] => getAvailableDrivers
)

プロパティは一つも出てこない。

query メソッド & prepare メソッド

よく分からないのでとりあえずコードを書いて動かしてみる。

<?php
$dns  = "mysql:host=localhost; dbname=fernwelt;";
$user = "root";
$pass = "password";
$sql  = "SELECT id, name FROM animals";

$pdo = new PDO($dns, $user, $pass);
$statement = $pdo->query($sql);

echo get_class($statement), "\n";
while ( $row = $statement->fetch(PDO::FETCH_ASSOC) )
{
  echo $row['id'], ' | ', $row['name'], "\n";
}

結果

PDOStatement
1 | tama
2 | poti
3 | sakanakun
4 | ari
<?php
$dns  = "mysql:host=localhost; dbname=fernwelt;";
$user = "root";
$pass = "password";
$sql  = "SELECT id, name FROM animals";

$pdo = new PDO($dns, $user, $pass);
$statement = $pdo->prepare($sql);
$statement->execute();

echo get_class($statement), "\n";
while ( $row = $statement->fetch(PDO::FETCH_ASSOC) )
{
  echo $row['id'], ' | ', $row['name'], "\n";
}

結果

PDOStatement
1 | tama
2 | poti
3 | sakanakun
4 | ari

どっちも結果は同じ。どちらも $sql に入っている SQL を実行している。 $statement に返ってきたものは両方とも PDOStatement オブジェクト。query() だと一行ですんでいる部分が、prepare() だと2行になっている。while分の中身は…また今度勉強しましょ ⇒ PDOStatement の各メソッドの使い方を知るべし

まぁこまかいコトはマニュアルで…でも今は大雑把でいいや。分からないことは後回しで次~

  • PHP: PDO::prepare - Manual
  • PHP: PDO::query - Manual
  • queryメソッド の返り値は PDOStatement オブジェクト
  • queryメソッドの第一引数 $statementSQLステートメント を入れる。これは適切にエスケープしなければならない。じゃなければSQLインジェクション脆弱性となる。
  • prepareメソッドの第一引数 $statementSQLステートメント を入れる。これはエスケープしなくても大丈夫らしい。

PDOと文字エンコーディング

前のDMLのコードはちゃんと動いているように見えるけどphpMyAdminにログインしてレコードを見ると日本語が文字化けしている…

これを解決するには… SET NAMES を使えばいい。 と思ったが問題があるらしい。sjis の場合 SET NAMES を使うと SQL インジェクション脆弱性となるそうな。

PHPからSET NAMESを使わない方が良い理由と対策まとめ | twk @ ふらっとによるとPHP5.2.3以降は set_mysql_charset() を使えば SET NAMES問題を回避できる…ってことらしいけども、ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション) - 徳丸浩の日記を読むと分かるように…PDOでは set_mysql_charset() が使えないので別の方法を使うことになる。 なんだか日本語が変なのは仕様をよく分かってないためテキトーに書いたから。ある程度勉強してからまだきちんと細かいところまで理解しないとなぁ…orz

とゆーことで理解はしてないけどひとまず解決。

XAMPP の場合は /xampp/mysql/bin/my.ini の中の [client] の項目に

default-character-set = utf8

という設定を追加する。そして以下のようなコードを実行すると文字化けせずに保存される。

<?php
$dns = "mysql:host=localhost; dbname=fernwelt;";
$user = "root";
$pass = "password";

// ドライバオプションの設定を配列にする
$options = array(
  PDO::MYSQL_ATTR_READ_DEFAULT_FILE => '/xampp/mysql/bin/my.ini',
  PDO::MYSQL_ATTR_READ_DEFAULT_GROUP => 'client'
);
// で、PDOのコンストラクタに突っ込む
$pdo = new PDO($dns, $user, $pass, $options);

$sql = "INSERT INTO animals(id,genus_name) VALUE(777,'文字化けするなよ!')";
$statement = $pdo->query($sql);

unset($pdo, $statement);

phpMyAdmin にログインして見ると文字化けせずに保存できましたとさ。めでたしめでたし。

memo

Share
関連記事