« PHP5.2.6(for Windows)に注意する | トップページ | PostgreSQL Conference 2008に参加する »

2008年5月29日 (木)

PDO::bindParamの挙動を知る

PDOを使って、よくあるコードを書いていた。

<?php
/* 前略 */
    public function getData($data_id, $user_id = -1)
    {
        // $pdo : PDO object
        $sql = "SELECT * FROM data" .
               "  WHERE data_id = :data_id" .
               // ↓テストのためにコメントアウトしてみる
//             "    AND user_id = :user_id" .
               "    AND valid   = true" ;
        $pdoStatement = $pdo->prepare($sql) ;
        $pdoStatement->bindParam(":data_id", $data_id, PDO::PARAM_INT) ;
        $pdoStatement->bindParam(":user_id", $user_id, PDO::PARAM_INT) ;
        if ($pdoStatement->execute())
        {
            return $pdoStatement->fetchAll() ;
        }
        return false ;
    }
/* 後略 */
?>

コメントの通りuser_idをチェックする条件を外してみた。つまり、プレイスホルダがないステートメントにパラメータをバインドしようとしている、という状態。bind先がないなら無視してくれてもよさそうなものだが…

Red Hat EL5 / PHP 5.2.5 / PostgreSQL 8.3.0
 >この環境では問題なく動作する。

WindowsXP / PHP 5.2.5(XAMPP 1.6.6a) / PostgreSQL 8.3.1
 >この環境ではWarningが発生し、クエリは失敗する。

Warning: PDOStatement::execute()
  [function.PDOStatement-execute]: SQLSTATE[HY093]:
  Invalid parameter number:
  number of bound variables does not match number of tokens in
  C:\path\to\appdir\modelclass.php on line ***

つまり、Windows環境でPDOを使う場合クエリのプレイスホルダとバインドするパラメータはきちんと数を合わせる必要がある、ということ。ただ、こちらの記事では逆で、Windows上では数があってなくてもOK / Fedoraに持ってくとNG、という挙動をしてる模様。どちらにしても数をあわせておけば問題ないので、そのようなコードを書くようにしよう。

逆を確認するためにこんなコードに変更してみる。プレイスホルダに対してバインドするパラメータが不足なので、当然エラーが出るはずなのだが…

<?php
/* 前略 */
        $sql = "SELECT * FROM data" .
               "  WHERE data_id = :data_id" .
               "    AND user_id = :user_id" .
               "    AND valid   = true" ;
        $stmt = $pdo->prepare($sql) ;
        $stmt->bindParam(":data_id", $data_id, PDO::PARAM_INT) ;
//      $stmt->bindParam(":user_id", $user_id, PDO::PARAM_INT) ;
/* 後略 */
?>

Red Hat EL5 / PHP 5.2.5 / PostgreSQL 8.3.0
 >クエリは失敗する。エラー情報をダンプしてみると…

<?php
var_dump($stmt->errorInfo()) ;
/*
array(3) {
  [0]=>
  string(5) "08P01"
  [1]=>
  int(7)
  [2]=>
  string(103) "ERROR:  bind message supplies 1 parameters, but prepared statement "pdo_pgsql_stmt_03d03950" requires 2"
}
*/
?>

WindowsXP / PHP 5.2.5(XAMPP 1.6.6a) / PostgreSQL 8.3.1
 >先ほどと同じWarningが発生し、クエリは失敗する。

RedHat上で同僚が開発していたものをWindows上に持ってきたらこのような挙動が判明した。

さらに。

プレイスホルダを使わないクエリを実行するコードはこんな感じになるのだが…

<?php
/* 前略 */
        $sql = "SELECT * FROM data" .
               "  WHERE valid   = true" .
               "  LIMIT 10" ;
        // プリペアドステートメントは不要なので「query」
        $stmt = $pdo->query($sql) ;
        if ($stmt->execute())
        {
            return $stmt->fetchAll() ;
        }
        return false ;
/* 後略 */
?>

WindowsXP / PHP 5.2.5(XAMPP 1.6.6a) / PostgreSQL 8.3.1
 >PDOStatement#executeが失敗する。
  PDO#queryPDO#prepare に変更すると成功する。

前代フレームワーク開発当時の環境はXAMPPではなく単品でインストールしたPHP(5.2.2)で、当然ながら問題なく動作していた。「PHP5.2.6(for Windows)に注意する」に続き「注意しよう」でまとめることになってしまうが、些細な環境の違いだと思っても主力級の命令の挙動が変わってしまうことがあるので注意しよう。

|

« PHP5.2.6(for Windows)に注意する | トップページ | PostgreSQL Conference 2008に参加する »

PDO」カテゴリの記事

コメント

なぜ query の後に execute を実行するのか?
成功の可否は errorCode でチェックすべきでは?

投稿: | 2008年8月 1日 (金) 18時55分

ご指摘ありがとうございます。
気になったので調べた結果、前代フレームワークの実装時に勘違いをして、
そのままイディオムとして使っているコードでした。
 # queryもprepare同様ステートメントの準備をするだけだと思っていた

あまりの恥ずかしさにエントリごと削りたい気分ですが、
近日調査して修正しようと思います。
どちらかというとWindows環境のほうが正しい挙動なのですね。

投稿: kwappa.856 | 2008年8月 1日 (金) 19時54分

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/509997/41274453

この記事へのトラックバック一覧です: PDO::bindParamの挙動を知る:

« PHP5.2.6(for Windows)に注意する | トップページ | PostgreSQL Conference 2008に参加する »